web-dev-qa-db-fra.com

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

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

517
Emanuil Rusev

Le HTTP_Host est obtenu à partir de en-tête de la requête HTTP et correspond à ce que le client a réellement utilisé comme "hôte cible" de la requête. Le SERVER_NAME est défini dans la configuration du serveur. Lequel utiliser dépend de ce dont vous avez besoin. Vous devez toutefois maintenant réaliser 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 que l’autre est une valeur contrôlée par le serveur qui est plus fiable. Vous devez toutefois vous assurer que le serveur Web en question a correctement configuré le SERVER_NAME. En prenant Apache HTTPD comme exemple, voici un extrait de sa documentation :

Si ServerName n'est pas 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 ServerName, le serveur utilisera le port de la requête 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 directive ServerName.


Mise à jour : après vérification la réponse de Pekka à votre question qui contient un lien vers réponse de bobince que PHP renverrait toujours la valeur de HTTP_Host pour SERVER_NAME, ce qui va à l'encontre de mes propres PHP 4.x + Apache HTTPD 1.2.x à partir de quelques il y a des années, j'ai soufflé un peu de poussière de mon environnement XAMPP sous Windows XP (Apache HTTPD 2.2.1 avec PHP 5.2.8), je l'ai démarré, créé un PHP page qui imprime les deux valeurs, a créé une application de test Java à l'aide de URLConnection pour modifier l'en-tête Host et les tests m'ont appris que c'était effectivement (à tort) le cas.

Après avoir d'abord soupçonné PHP et creusé quelques rapports de bugs 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-tête HTTP Host lorsque SERVER_NAME a été demandé. J'ai donc creusé rapports de bugs Apache HTTPD en utilisant divers mots clés en ce qui concerne le sujet et j'ai finalement trouvé un bogue lié . Ce comportement a été introduit depuis autour d'Apache HTTPD 1.3. Vous devez définir la directive UseCanonicalName sur on dans l'entrée <VirtualHost> de ServerName dans httpd.conf (vérifiez également l'avertissement situé au bas de le document !).

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

Cela a fonctionné pour moi.

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

763
BalusC

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

SERVER_NAME provient de la définition du serveur VirtualHost et est donc considéré comme plus fiable. Toutefois, il peut également être manipulé de l’extérieur dans certaines conditions liées à la configuration de votre serveur Web: voir ceci ceci SO question qui traite des aspects de sécurité des deux variantes.

Vous ne devriez pas compter non plus sur vous pour être en sécurité. Cela dit, l’utilisation dépend vraiment de ce que vous voulez faire. Si vous souhaitez déterminer le domaine sur lequel votre script est exécuté, vous pouvez utiliser HTTP_Host en toute sécurité, tant que les valeurs non valides provenant d'un utilisateur malveillant ne peuvent rien casser.

64
Pekka 웃

Comme je l’ai mentionné dans cette réponse , si le serveur fonctionne sur un port autre que 80 (comme cela peut être courant sur un ordinateur de développement/intranet), alors _HTTP_Host_ contient le port, tandis que _SERVER_NAME_ ne fait pas.

_$_SERVER['HTTP_Host'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'
_

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

Notez que _HTTP_Host_ ne ne contient pas _:443_ lorsqu’il est exécuté sur HTTPS (à moins que vous n’exécutiez sur un port non standard, que je n'ai pas testé).

Comme d'autres l'ont déjà noté, l'utilisation de IPv6 diffère également:

_$_SERVER['HTTP_Host'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
_
51
Simon East

Veuillez noter que si vous voulez utiliser IPv6, vous voudrez probablement utiliser HTTP_Host plutô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 ne s'applique que si vous accédez au serveur sans nom d'hôte.

26
Daniel Marschall

si vous voulez vérifier via un server.php ou ce que vous voulez appeler avec ce qui suit:

<?php

phpinfo(INFO_VARIABLES);

?>

ou

<?php

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

print_r($_SERVER);

?>

Ensuite, accédez-y avec toutes les URL valides de votre site et vérifiez la différence.

6
stevewh

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

5
Rowland Shaw

En supposant que l'un d'entre eux dispose d'une configuration simple (CentOS 7, Apache 2.4.x et PHP 5.6.20) et d'un seul site Web (sans hébergement virtuel) ...

Dans le sens PHP, _$_SERVER['SERVER_NAME']_ est un élément PHP inscrit dans le superglobal _$_SERVER_ en fonction de votre directive de configuration Apache (directive _**ServerName**_ 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-tête HTTP Host. Traitez cela comme une entrée utilisateur. Filtrer et valider avant utilisation.

Voici un exemple où j'utilise _$_SERVER['SERVER_NAME']_ comme base de comparaison. La méthode suivante provient d'une classe enfant concrète que j'ai nommée ServerValidator (enfant de Validator). ServerValidator vé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 éléments de filtrage et de validation des éléments pertinents de $ _SERVER 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 valeur _$_SERVER['HTTP_Host']_ (finalement dérivée de l'en-tête HTTP Host demandé) correspond à _$_SERVER['SERVER_NAME']_.

Maintenant, j'utilise le langage superglobal pour expliquer mon exemple, mais c'est simplement parce que certaines personnes ne connaissent pas _INPUT_GET_, _INPUT_POST_ et _INPUT_SERVER_ en ce qui concerne filter_input_array() =.

En bout de ligne, je ne gère pas les demandes POST sur mon serveur, sauf si tous quatre conditions sont remplies. Par conséquent, en termes de POST requêtes, incapacité à fournir un en-tête HTTP Host (présence testée pour les versions antérieures) Doom pour strict HTTP 1.0 navigateurs. De plus, l'hôte demandé [doit correspondre à la valeur pour ServerName dans le httpd.conf, et , par extension, la valeur de $_SERVER('SERVER_NAME') dans le superglobal _$_SERVER_. Encore une fois, j'utiliserais _INPUT_SERVER_ avec les fonctions de filtrage PHP, mais vous comprendrez ma dérive.

Gardez à l'esprit qu'Apache utilise fréquemment ServerName dans redirections standard (par exemple, en laissant la barre oblique de fin d'une URL: Exemple, http://www.foo.com = devient http://www.foo.com/), même si vous n'utilisez pas la réécriture d'URL.

J'utilise _$_SERVER['SERVER_NAME']_ comme norme, pas _$_SERVER['HTTP_Host']_. Il y a beaucoup de va-et-vient sur cette question. _$_SERVER['HTTP_Host']_ pourrait être vide, cela ne devrait donc pas servir de base à la création de conventions de code telles que ma méthode publique ci-dessus. Mais, le fait que les deux puissent ê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 d'Apache et la version PHP).

2
Anthony Rutledge

Il m'a fallu un certain temps pour comprendre ce que les gens entendaient par "SERVER_NAME est plus fiable". J'utilise un serveur partagé et je n'ai pas accès aux directives de l'hôte virtuel. Donc, j'utilise mod_rewrite dans .htaccess pour mapper différents HTTP_Hosts vers différents répertoires. Dans ce cas, c'est HTTP_Host qui a du sens.

La situation est similaire si l'on utilise des hôtes virtuels nommés: la directive ServerName au sein d'un hôte virtuel indique simplement quel nom d'hôte sera mappé sur 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 correspondre à un nom sur le serveur, qui est lui-même mappé à un répertoire. Que le mappage soit effectué avec les directives de l'hôte virtuel ou avec les règles htaccess mod_rewrite est secondaire ici. Dans ces cas, HTTP_Host sera identique à 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 uniquement dans ce cas, SERVER_NAME et HTTP_Host peuvent être différents, car le client sélectionne maintenant le serveur IP, pas par le nom. En effet, il peut y avoir des configurations spéciales où 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.

2
Dominic108

Comme BalusC l'a déclaré, SERVER_NAME n'est pas fiable et peut être modifié dans Apache Config, configuration du nom du serveur du serveur et pare-feu pouvant se situer entre vous et le serveur.

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

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