Quelle est la différence entre HTTP_HOST et SERVER_NAME en PHP?

533

Quelle est la différence entre PHP HTTP_HOSTet SERVER_NAMEPHP?

où:

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

Quand envisageriez-vous d'utiliser l'un sur l'autre et pourquoi?

Emanuil Rusev
la source
14
"Je choisis normalement HTTP_HOST, afin que l'utilisateur reste sur le nom d'hôte exact sur lequel il a commencé. Par exemple, si j'ai le même site sur un domaine .com et .org, je ne veux pas envoyer quelqu'un de .org vers .com, en particulier s'ils peuvent avoir des jetons de connexion sur .org qu'ils perdraient s'ils étaient envoyés à l'autre domaine. " - Ceci et quelques autres points intéressants de stackoverflow.com/questions/1459739/…
Yarin
5
@Yarin, n'oubliez pas de mettre en liste blanche-vérifier les résultats deHTTP_HOST . Sinon, un attaquant peut mettre n'importe quelle valeur dans la requête HTTP Host:et obliger le serveur à l'accepter.
Pacerier
6
Débutants: Cette question fait référence aux valeurs généralement obtenues via $_SERVER['HTTP_HOST']ou$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Réponses:

780

Le HTTP_HOSTest obtenu à partir de l' en-tête de requête HTTP et c'est ce que le client a réellement utilisé comme "hôte cible" de la requête. Le SERVER_NAMEest défini dans la configuration du serveur. Lequel utiliser dépend de ce dont vous avez besoin. Cependant, vous devez maintenant vous rendre compte que l'une est une valeur contrôlée par le client qui peut donc ne pas être fiable pour une utilisation dans la logique métier et l'autre est une valeur contrôlée par le serveur qui est plus fiable. Vous devez cependant vous assurer que le serveur Web en question est SERVER_NAMEcorrectement configuré. Prenant Apache HTTPD comme exemple, voici un extrait de sa documentation :

Si non ServerNameest spécifié, le serveur tente de déduire le nom d'hôte en effectuant une recherche inversée sur l'adresse IP. Si aucun port n'est spécifié dans le ServerName, le serveur utilisera le port de la demande entrante. Pour une fiabilité et une prévisibilité optimales, vous devez spécifier un nom d'hôte et un port explicites à l'aide de la ServerNamedirective.


Mise à jour : après avoir vérifié la réponse de Pekka à votre question qui contient un lien vers la réponse de Bobince pour laquelle PHP retournerait toujours HTTP_HOSTla valeur de SERVER_NAME, ce qui va à l'encontre de mes propres expériences PHP 4.x + Apache HTTPD 1.2.x d'il y a quelques années , J'ai soufflé de la poussière de mon environnement XAMPP actuel sur Windows XP (Apache HTTPD 2.2.1 avec PHP 5.2.8), l'ai démarré, créé une page PHP qui imprime les deux valeurs, créé une application de test Java en utilisant URLConnectionpour modifier l'en- Hosttête et les tests m'ont appris que c'est effectivement (incorrectement) le cas.

Après avoir soupçonné PHP pour la première fois et avoir fouillé dans certains rapports de bogues PHP concernant le sujet, j'ai appris que la racine du problème se trouvait dans le serveur Web utilisé, qu'il renvoyait incorrectement l'en- Hosttête HTTP lors de la SERVER_NAMEdemande. J'ai donc fouillé dans les rapports de bogues Apache HTTPD en utilisant divers mots clés concernant le sujet et j'ai finalement trouvé un bogue connexe . Ce comportement a été introduit depuis environ Apache HTTPD 1.3. Vous devez définir UseCanonicalNamedirective ondans l' <VirtualHost>entrée du ServerNamedans httpd.conf(vérifiez aussi la mise en garde au bas du document !).

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

Cela a fonctionné pour moi.

En résumé, SERVER_NAMEest plus fiable, mais vous êtes dépendant de la configuration du serveur!

BalusC
la source
5
D'accord, cela résout mon problème, qui n'est pas lié à l'OP mais pertinent. J'étais très préoccupé par les problèmes de sécurité en utilisant tout ce qu'un navigateur pouvait fournir. Cette réponse a été d'une immense aide. Merci d'avoir pris le temps de le préparer.
Yitzhak
2
Pourquoi dites-vous que HTTP_HOST n'est pas fiable? Oui, il est fourni par l'utilisateur, mais si l'utilisateur donne une valeur fausse, votre configuration de serveur retournerait automatiquement 503 et votre script PHP ne serait même pas exécuté!
Pacerier
1
@Pacerier: au moment d'écrire cette réponse, ce n'était pas le cas. Les versions sont mentionnées dans la réponse. Je ne suis plus au courant de PHP, donc je ne peux pas dire s'il a effectivement changé dans une version plus récente.
BalusC
2
Un moyen simple de tromper Apache de WinXP consiste à ajouter une ligne au fichier «hosts» indiquant que l'IP du serveur est attribuée à un autre domaine, comme ceci: «127.0.0.1 mydomain.com». Je l'ai utilisé beaucoup de fois pour montrer un site Web local incitant mon public à penser que ma connexion Internet et mon site sont chargés très rapidement. Vous pouvez aller dans l'autre sens, et tromper Apache pour penser qu'il fonctionne localement, avec "173.194.41.5 localhost", donc vous ne devez jamais faire entièrement confiance à SERVER_NAME à moins d'être sûr que votre Apache est bien configuré.
vicenteherrera
1
Je veux juste ajouter que NGINX + PHP-FPM renvoie la valeur définie par la server_namedirective. Surtout si aucun server_namen'est défini, il _SERVER["SERVER_NAME"]sera également vide.
white_gecko
69

HTTP_HOSTest l'hôte cible envoyé par le client. Il peut être manipulé librement par l'utilisateur. Ce n'est pas un problème pour envoyer une demande à votre site demandant une HTTP_HOSTvaleur de www.stackoverflow.com.

SERVER_NAMEprovient de la VirtualHostdéfinition du serveur et est donc considéré comme plus fiable. Cependant, il peut également être manipulé de l'extérieur sous certaines conditions liées à la configuration de votre serveur Web: voir cette question SO qui traite des aspects de sécurité des deux variantes.

Vous ne devriez pas compter sur l'un ou l'autre pour être en sécurité. Cela dit, ce que vous utilisez dépend vraiment de ce que vous voulez faire. Si vous souhaitez déterminer sur quel domaine votre script s'exécute, vous pouvez l'utiliser en toute sécurité HTTP_HOSTtant que les valeurs non valides provenant d'un utilisateur malveillant ne peuvent rien casser.

Pekka
la source
8
Oui, mais une demande demandant une valeur HTTP_HOST de www.stackoverflow.com serait rejetée par la plupart des serveurs HTTP à l'avance, de sorte que le script PHP ne verrait même pas la demande!
Pacerier
2
@Pacerier true, mais pas toujours si le serveur n'est pas correctement configuré.
Pekka
1
Comme mentionné dans le poste de BalusC, lorsque vous accédez à un serveur virtuel Apache par IP, les deux de ces variables contiennent l'adresse IP (par défaut), pas le nom réel du serveur. Vous devez utiliser UseCanonicalName ondans httpd.conf pour forcer SERVER_NAMEà être le nom réel du serveur.
Simon East
@Pekka 웃, Si le serveur n'est pas correctement configuré, $_SERVER['SERVER_NAME']cela ne fonctionnerait pas aussi bien . Un serveur mal configuré sera défini en $_SERVER['SERVER_NAME']fonction de la valeur de la Host:demande du client . Les deux sont égaux.
Pacerier
Bonne réponse, mais je n'assumerais pas d'hébergement virtuel.
Anthony Rutledge
55

Comme je l'ai mentionné dans cette réponse , si le serveur s'exécute sur un port autre que 80 (comme cela peut être courant sur une machine de développement / intranet), il HTTP_HOSTcontient alors le port, alors que ce SERVER_NAMEn'est pas le cas.

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

(C'est du moins ce que j'ai remarqué dans les hôtes virtuels basés sur le port Apache)

Notez que HTTP_HOSTcela ne contient pas:443 lors de l'exécution sur HTTPS (sauf si vous exécutez sur un port non standard, que je n'ai pas testé).

Comme d'autres l'ont noté, les deux diffèrent également lors de l'utilisation d'IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Simon East
la source
2
Quand vont-ils corriger ce comportement insidieux?
Pacerier
27

Veuillez noter que si vous souhaitez utiliser IPv6, vous préférez probablement utiliser HTTP_HOSTplutôt que SERVER_NAME. Si vous entrez http://[::1]/les variables d'environnement seront les suivantes:

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

Cela signifie que si vous faites un mod_rewrite par exemple, vous pourriez obtenir un résultat désagréable. Exemple pour une redirection 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}/

Cela s'applique UNIQUEMENT si vous accédez au serveur sans nom d'hôte.

Daniel Marschall
la source
1
SiteGround, dans leur code de redirection http vers https interne, utilisezhttps://%{SERVER_NAME}%{REQUEST_URI}
IXN
6

Si vous souhaitez vérifier via un server.php ou autre chose, vous voulez l'appeler comme suit:

<?php
    phpinfo(INFO_VARIABLES);
?>

ou

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

    print_r($_SERVER);
?>

Accédez-y ensuite avec toutes les URL valides de votre site et découvrez la différence.

stewh
la source
5

Cela dépend de ce que je veux découvrir. SERVER_NAME est le nom d'hôte du serveur, tandis que HTTP_HOST est l'hôte virtuel auquel le client s'est connecté.

Rowland Shaw
la source
4
Pas exactement vrai Rowland, SERVER_NAMEest généralement le nom du VirtualHost, pas le serveur lui-même. Et dans Apache, SERVER_NAMEest souvent HTTP_HOSTrempli avec la même valeur que (voir la réponse de BalusC).
Simon East
1
@Simon, Étant donné que les hôtes Mosts sont maintenant VirtualHost, que voulez-vous dire par le nom de "le serveur lui-même"?
Pacerier
Si vous exécutez un serveur privé virtuel (VPS) avec un site Web, vous n'avez pas besoin de supposer que cela SERVER_NAMEs'applique à un hôte virtuel. Cependant, on peut toujours utiliser une configuration d'hôte virtuel pour un site. Beaucoup de gens utilisent l'hébergement partagé, donc je vois votre point.
Anthony Rutledge
2

Il m'a fallu un certain temps pour comprendre ce que les gens entendaient par « SERVER_NAMEest plus fiable». J'utilise un serveur partagé et je n'ai pas accès aux directives d'hôte virtuel. Donc, j'utilise mod_rewrite dans .htaccess pour mapper différents HTTP_HOSTs à différents répertoires. Dans ce cas, c'est HTTP_HOSTsignificatif.

La situation est similaire si l'on utilise des hôtes virtuels basés sur le nom: la ServerNamedirective dans un hôte virtuel indique simplement quel nom d'hôte sera mappé à cet hôte virtuel. L'essentiel est que, dans les deux cas, le nom d'hôte fourni par le client lors de la demande ( HTTP_HOST), doit être mis en correspondance avec un nom au sein du serveur, lui-même mappé à un répertoire. Que le mappage soit effectué avec des directives d'hôte virtuel ou avec des règles htaccess mod_rewrite est secondaire ici. Dans ces cas, HTTP_HOSTsera le même que SERVER_NAME. Je suis heureux qu'Apache soit configuré de cette façon.

Cependant, la situation est différente avec les hôtes virtuels basés sur IP. Dans ce cas et seulement dans ce cas, SERVER_NAMEet HTTP_HOSTpeut être différent, car maintenant le client sélectionne le serveur par l'IP, pas par le nom. En effet, il peut y avoir des configurations spéciales lorsque cela est important.

Donc, à partir de maintenant, je vais utiliser SERVER_NAME, juste au cas où mon code serait porté dans ces configurations spéciales.

Dominic108
la source
2

En supposant qu'un a une configuration simple (CentOS 7, Apache 2.4.x et PHP 5.6.20) et un seul site Web (ne supposant pas d'hébergement virtuel) ...

Au sens PHP, $_SERVER['SERVER_NAME']est un élément que PHP enregistre dans le $_SERVERsuperglobal en fonction de votre configuration Apache ( **ServerName**directive avec UseCanonicalName On) dans httpd.conf (que ce soit à partir d'un fichier de configuration d'hôte virtuel inclus, peu importe, etc ...). HTTP_HOST est dérivé de l'en- hosttête HTTP . Traitez cela comme une entrée utilisateur. Filtrez et validez avant d'utiliser.

Voici un exemple où j'utilise $_SERVER['SERVER_NAME']comme base pour une comparaison. La méthode suivante est issue d'une classe enfant concrète que j'ai créée nommée ServerValidator(enfant de Validator). ServerValidatorvérifie six ou sept éléments dans $ _SERVER avant de les utiliser.

Pour déterminer si la requête HTTP est POST, j'utilise cette méthode.

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

Au moment où cette méthode est appelée, tous les filtrages et validations des éléments $ _SERVER pertinents se seraient produits (et les propriétés pertinentes définies).

La ligne ...

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

... vérifie que la $_SERVER['HTTP_HOST']valeur (finalement dérivée de l' hosten-tête HTTP demandé ) correspond $_SERVER['SERVER_NAME'].

Maintenant, je me sers de parler superglobale pour expliquer mon exemple, mais c'est juste parce que certaines personnes ne connaissent pas INPUT_GET, INPUT_POSTet INPUT_SERVERen ce qui concerne filter_input_array().

L'essentiel est que je ne traite pas les demandes POST sur mon serveur à moins que les quatre conditions ne soient remplies. Par conséquent, en termes de requêtes POST, le fait de ne pas fournir un en- hosttête HTTP (présence testée pour plus tôt) est fatal pour les navigateurs HTTP 1.0 stricts . De plus, l'hôte demandé doit correspondre à la valeur de ServerNamedans le httpd.conf et, par extension, à la valeur de $_SERVER('SERVER_NAME')dans le $_SERVERsuperglobal. Encore une fois, j'utiliserais INPUT_SERVERavec les fonctions de filtre PHP, mais vous attrapez ma dérive.

Gardez à l' esprit que Apache utilise fréquemment ServerNamedans standards réoriente ( par exemple en laissant le slash hors URL: Exemple, http://www.foo.com devenir http://www.foo.com/ ), même si vous n'êtes pas en utilisant la réécriture d'URL.

J'utilise $_SERVER['SERVER_NAME']comme standard, non $_SERVER['HTTP_HOST']. Il y a beaucoup de va-et-vient sur cette question. $_SERVER['HTTP_HOST']pourrait être vide, donc cela ne devrait pas être la base pour créer des conventions de code telles que ma méthode publique ci-dessus. Mais, tout simplement parce que les deux peuvent être définis ne garantit pas qu'ils seront égaux. Le test est le meilleur moyen de savoir avec certitude (en gardant à l'esprit la version Apache et la version PHP).

Anthony Rutledge
la source
0

Comme balusC l'a dit, SERVER_NAME n'est pas fiable et peut être modifié dans la configuration d'apache, la configuration du nom de serveur du serveur et le pare-feu qui peut se trouver entre vous et le serveur.

La fonction suivante retourne toujours un véritable hôte (hôte tapé par l'utilisateur) sans port et c'est presque fiable:

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

$ _SERVER ['SERVER_NAME'] est basé sur la configuration de vos serveurs Web. $ _SERVER ['HTTP_HOST'] est basé sur la demande du client.

Vitalie
la source