Qual è la differenza tra HTTP_HOST e SERVER_NAME in PHP?


533

Qual è la differenza tra HTTP_HOSTe SERVER_NAMEin PHP?

dove:

  • HTTP_POST === $_SERVER['HTTP_HOST']
  • SERVER_NAME === $_SERVER['SERVER_NAME']

Quando valuteresti di usarne uno rispetto all'altro e perché?


14
"Normalmente scelgo HTTP_HOST, in modo che l'utente rimanga sul nome host esatto su cui ha iniziato. Ad esempio, se ho lo stesso sito su un dominio .com e .org, non voglio inviare qualcuno da .org a .com, in particolare se potrebbero avere token di accesso su .org che perderebbero se inviati a un altro dominio. " - Questo e alcuni altri punti interessanti da stackoverflow.com/questions/1459739/...
Yarin

5
@Yarin, non dimenticare di autorizzare-verificare i risultati diHTTP_HOST . Altrimenti un utente malintenzionato può inserire qualsiasi valore nella Host:richiesta HTTP e fare in modo che il server lo accetti.
Pacerier

6
Principianti: questa domanda si riferisce ai valori generalmente ottenuti tramite $_SERVER['HTTP_HOST']o$_SERVER['SERVER_NAME']
Gregory Cosmo Haun,

Risposte:


780

La HTTP_HOSTsi ottiene dalla intestazione di richiesta HTTP e questo è ciò che il cliente effettivamente utilizzato come "host di destinazione" della richiesta. Il SERVER_NAMEè definito nella configurazione del server. Quale usare dipende da ciò di cui hai bisogno. Tuttavia, è ora necessario rendersi conto che l'uno è un valore controllato dal client che potrebbe quindi non essere affidabile per l'uso nella logica aziendale e l'altro è un valore controllato dal server che è più affidabile. Tuttavia, è necessario assicurarsi che il server Web in questione sia SERVER_NAMEconfigurato correttamente. Prendendo Apache HTTPD come esempio, ecco un estratto dalla sua documentazione :

Se non ServerNameviene specificato no , il server tenta di dedurre il nome host eseguendo una ricerca inversa sull'indirizzo IP. Se non viene specificata alcuna porta in ServerName, il server utilizzerà la porta dalla richiesta in arrivo. Per affidabilità e prevedibilità ottimali, è necessario specificare un nome host e una porta espliciti utilizzando la ServerNamedirettiva.


Aggiornamento : dopo aver verificato la risposta di Pekka alla tua domanda che contiene un link alla risposta di Bobince per cui PHP restituirebbe sempre HTTP_HOSTil valore SERVER_NAME, il che va contro la mia esperienza PHP 4.x + Apache HTTPD 1.2.x di un paio di anni fa , Ho scaricato un po 'di polvere dal mio attuale ambiente XAMPP su Windows XP (Apache HTTPD 2.2.1 con PHP 5.2.8), l'ho avviato, ho creato una pagina PHP che stampa entrambi i valori, ho creato un'applicazione di test Java usando URLConnectionper modificare l' Hostintestazione e i test mi hanno insegnato che questo è effettivamente (erroneamente) il caso.

Dopo aver sospettato per la prima volta PHP e aver scavato in alcune segnalazioni di bug PHP riguardanti l'argomento, ho appreso che la radice del problema è nel web server utilizzato, che ha restituito erroneamente l' Hostintestazione HTTP quando è SERVER_NAMEstato richiesto. Così ho scavato nelle segnalazioni di bug HTTPD di Apache usando varie parole chiave riguardanti l'argomento e alla fine ho trovato un bug correlato . Questo comportamento è stato introdotto da Apache HTTPD 1.3. È necessario impostare la UseCanonicalNamedirettiva su onnella <VirtualHost>voce di ServerNamein httpd.conf(controllare anche l'avvertenza nella parte inferiore del documento !).

<VirtualHost *>
    ServerName example.com
    UseCanonicalName on
</VirtualHost> 

Questo ha funzionato per me.

Riassunto, SERVER_NAMEè più affidabile, ma dipende dalla configurazione del server!


5
Va bene, questo risolve il mio problema, che non è correlato all'OP ma pertinente. Ero molto preoccupato per i problemi di sicurezza usando tutto ciò che un browser poteva fornire. Questa risposta è stata di enorme aiuto. Grazie per aver dedicato del tempo per metterlo insieme.
Yitzhak,

2
Perché dici che HTTP_HOST non è affidabile? Sì, viene fornito dall'utente, ma se l'utente fornisce un valore falso, la configurazione del server restituirà automaticamente 503 e lo script PHP non verrebbe nemmeno eseguito!
Pacerier,

1
@Pacerier: al momento della stesura di questa risposta, non è così. Le versioni sono menzionate nella risposta. Non tengo più il passo con PHP, quindi non posso dire se è davvero cambiato in una versione più recente.
BalusC

2
Un modo semplice per ingannare Apache da WinXP è aggiungere una riga al file 'hosts' affermando che l'IP del server è assegnato a un altro dominio, come questo: "127.0.0.1 mydomain.com". L'ho usato molte volte per mostrare un sito Web locale che induce il mio pubblico a pensare di avere una connessione Internet e un sito caricati molto velocemente. Potresti andare dall'altra parte e indurre Apache a pensare che stia funzionando localmente, con "173.194.41.5 localhost", quindi non dovresti mai fidarti completamente di SERVER_NAME a meno che tu non sia sicuro che Apache sia ben configurato.
Vicenteherrera,

1
Voglio solo aggiungere che NGINX + PHP-FPM restituisce il valore impostato dalla server_namedirettiva. Soprattutto se non server_nameè impostato anche _SERVER["SERVER_NAME"]sarà vuoto.
white_gecko

69

HTTP_HOSTè l'host di destinazione inviato dal client. Può essere manipolato liberamente dall'utente. Non è un problema inviare una richiesta al tuo sito chiedendo un HTTP_HOSTvalore di www.stackoverflow.com.

SERVER_NAMEderiva dalla VirtualHostdefinizione del server ed è quindi considerato più affidabile. Tuttavia, può anche essere manipolato dall'esterno in determinate condizioni relative alla configurazione del server Web: vedere questa domanda SO che tratta gli aspetti di sicurezza di entrambe le varianti.

Non dovresti fare affidamento su nessuno dei due per essere sicuro. Detto questo, cosa usare dipende davvero da cosa vuoi fare. Se si desidera determinare su quale dominio è in esecuzione lo script, è possibile utilizzare in modo sicuro HTTP_HOSTpurché valori non validi provenienti da un utente malintenzionato non possano interrompere nulla.


8
Sì, ma una richiesta che richiede un valore HTTP_HOST di www.stackoverflow.com verrebbe rifiutata dalla maggior parte dei server HTTP in anticipo, quindi lo script PHP non vedrebbe nemmeno la richiesta!
Pacerier,

2
@Pacerier true, ma non sempre se il server non è configurato correttamente.
Pekka,

1
Come menzionato nel post di BalusC, quando si accede a un virtualhost Apache tramite IP, entrambe queste variabili contengono l'IP (per impostazione predefinita), non il nome effettivo del server. Devi usare UseCanonicalName onhttpd.conf per forzare SERVER_NAMEad essere il nome effettivo del server.
Simon East,

@Pekka 웃, Se il server non è configurato correttamente, $_SERVER['SERVER_NAME']non funzionerebbe altrettanto bene . Un server configurato in modo errato verrà impostato in $_SERVER['SERVER_NAME']base al valore della Host:richiesta del client . Entrambi sono uguali.
Pacerier

Buona risposta, ma non presumo l'hosting virtuale.
Anthony Rutledge,

55

Come ho detto in questa risposta , se il server funziona su una porta diversa da 80 (come potrebbe essere comune su una macchina di sviluppo / intranet), allora HTTP_HOSTcontiene la porta, mentre SERVER_NAMEnon lo è.

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(Almeno questo è quello che ho notato negli host virtuali basati su porte Apache)

Si noti che HTTP_HOSTnon non contiene :443quando viene eseguito su HTTPS (a meno che non si sta eseguendo su una porta non standard, che non ho ancora testato).

Come altri hanno notato, i due differiscono anche quando si utilizza IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'

2
Quando risolveranno questo comportamento insidioso?
Pacerier

27

Si noti che se si desidera utilizzare IPv6, probabilmente si desidera utilizzare HTTP_HOSTanziché SERVER_NAME. Se inserisci http://[::1]/le variabili di ambiente saranno le seguenti:

HTTP_HOST = [::1]
SERVER_NAME = ::1

Ciò significa che se si fa un mod_rewrite per esempio, si potrebbe ottenere un risultato sgradevole. Esempio per un reindirizzamento SSL:

# SERVER_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/

# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/

Questo vale SOLO se si accede al server senza un nome host.


1
SiteGround, nel loro codice interno di reindirizzamento http per https, utilizzahttps://%{SERVER_NAME}%{REQUEST_URI}
IXN

6

Se vuoi controllare attraverso un server.php o altro, vuoi chiamarlo con il seguente:

<?php
    phpinfo(INFO_VARIABLES);
?>

o

<?php
    header("Content-type: text/plain");

    print_r($_SERVER);
?>

Quindi accedilo con tutti gli URL validi per il tuo sito e controlla la differenza.


5

Dipende da cosa voglio scoprire. SERVER_NAME è il nome host del server, mentre HTTP_HOST è l'host virtuale a cui si è connesso il client.


4
Rowland non è esattamente vero, di SERVER_NAMEsolito è il nome di VirtualHost, non il server stesso. E in Apache, SERVER_NAMEè spesso popolato con lo stesso valore di HTTP_HOST(vedi la risposta di BalusC).
Simon East,

1
@Simon, Dato che gli host mosts sono ora VirtualHost, cosa vorresti dire con il nome di "server stesso"?
Pacerier

Se si esegue un server privato virtuale (VPS) con un sito Web, non è necessario presupporre che si SERVER_NAMEapplichi a un host virtuale. Tuttavia, è ancora possibile utilizzare una configurazione host virtuale per un sito. Molte persone usano l'hosting condiviso, quindi vedo il tuo punto.
Anthony Rutledge,

2

Mi ci è voluto un po 'di tempo per capire cosa si intendesse per " SERVER_NAMEè più affidabile". Uso un server condiviso e non ho accesso alle direttive dell'host virtuale. Quindi, uso mod_rewrite in .htaccess per mappare HTTP_HOSTs differenti a directory diverse. In tal caso, HTTP_HOSTè significativo.

La situazione è simile se si usano host virtuali basati sul nome: la ServerNamedirettiva all'interno di un host virtuale dice semplicemente quale nome host verrà mappato su questo host virtuale. La linea di fondo è che, in entrambi i casi, il nome host fornito dal client durante la richiesta ( HTTP_HOST), deve corrispondere a un nome all'interno del server, che è esso stesso mappato a una directory. Se il mapping viene eseguito con direttive host virtuali o con regole htaccess mod_rewrite è secondario qui. In questi casi, HTTP_HOSTsarà lo stesso di SERVER_NAME. Sono contento che Apache sia configurato in questo modo.

Tuttavia, la situazione è diversa con gli host virtuali basati su IP. In questo caso e solo in questo caso, SERVER_NAMEe HTTP_HOSTpuò essere diverso, perché ora il client seleziona il server dall'IP, non dal nome. In effetti, potrebbero esserci configurazioni speciali in cui questo è importante.

Quindi, a partire da ora, userò SERVER_NAME, nel caso in cui il mio codice sia portato in queste configurazioni speciali.


2

Supponendo che uno abbia una configurazione semplice (CentOS 7, Apache 2.4.xe PHP 5.6.20) e un solo sito Web (non assumendo l'hosting virtuale) ...

In senso PHP, $_SERVER['SERVER_NAME']è un elemento che PHP registra nel $_SERVERsuperglobal in base alla configurazione di Apache ( **ServerName**direttiva con UseCanonicalName On) in httpd.conf (sia esso da un file di configurazione dell'host virtuale incluso, qualunque cosa, ecc ...). HTTP_HOST è derivato dall'intestazione HTTP host. Considera questo come input dell'utente. Filtra e convalida prima dell'uso.

Ecco un esempio di dove uso $_SERVER['SERVER_NAME']come base per un confronto. Il seguente metodo proviene da una classe figlio concreta che ho nominato ServerValidator(figlio di Validator). ServerValidatorcontrolla sei o sette elementi in $ _SERVER prima di usarli.

Nel determinare se la richiesta HTTP è POST, utilizzo questo metodo.

public function isPOST()
{
    return (($this->requestMethod === 'POST')    &&  // Ignore
            $this->hasTokenTimeLeft()            &&  // Ignore
            $this->hasSameGETandPOSTIdentities() &&  // Ingore
            ($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}

Quando viene chiamato questo metodo, si sarebbero verificati tutti i filtri e la convalida dei relativi elementi $ _SERVER (e le relative proprietà impostate).

La linea ...

($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')

... verifica che il $_SERVER['HTTP_HOST']valore (alla fine derivato dall'intestazione hostHTTP richiesta ) corrisponda $_SERVER['SERVER_NAME'].

Ora, io sto usando parlare superglobale di spiegare il mio esempio, ma che è solo perché alcune persone non hanno familiarità con INPUT_GET, INPUT_POSTe INPUT_SERVERper quanto riguarda filter_input_array().

La linea di fondo è che non gestisco le richieste POST sul mio server a meno che non siano soddisfatte tutte e quattro le condizioni. Quindi, in termini di richieste POST, la mancata fornitura di hostun'intestazione HTTP (presenza verificata per precedenti) indica un destino per i browser HTTP 1.0 rigidi . Inoltre, l'host richiesto deve corrispondere al valore per ServerNamein httpd.conf e, per estensione, al valore per $_SERVER('SERVER_NAME')nel $_SERVERsuperglobal. Ancora una volta, userei INPUT_SERVERcon le funzioni del filtro PHP, ma tu catturi la mia deriva.

Tieni presente che Apache utilizza spesso ServerNamenei reindirizzamenti standard (come lasciare la barra finale da un URL: Esempio, http://www.foo.com che diventa http://www.foo.com/ ), anche se non lo sei usando la riscrittura degli URL.

Uso $_SERVER['SERVER_NAME']lo standard, no $_SERVER['HTTP_HOST']. C'è molto avanti e indietro su questo problema. $_SERVER['HTTP_HOST']potrebbe essere vuoto, quindi questa non dovrebbe essere la base per la creazione di convenzioni di codice come il mio metodo pubblico sopra. Ma solo perché entrambi possono essere impostati non garantisce che saranno uguali. Il test è il modo migliore per saperlo con certezza (tenendo presente la versione di Apache e la versione di PHP).


0

Come ha detto balusC SERVER_NAME non è affidabile e può essere modificato nella configurazione di Apache, nella configurazione del nome del server e del firewall che può essere tra l'utente e il server.

La seguente funzione restituisce sempre l'host reale (host digitato dall'utente) senza porta ed è quasi affidabile:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}

0

$ _SERVER ['SERVER_NAME'] si basa sulla configurazione del tuo server web. $ _SERVER ['HTTP_HOST'] si basa sulla richiesta del client.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.