¿Cuál es la diferencia entre HTTP_HOST y SERVER_NAME en PHP?

533

¿Cuál es la diferencia entre HTTP_HOSTy SERVER_NAMEen PHP?

dónde:

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

¿Cuándo considerarías usar uno sobre el otro y por qué?

Emanuil Rusev
fuente
14
"Normalmente elijo HTTP_HOST, para que el usuario permanezca en el nombre de host exacto en el que comenzó. Por ejemplo, si tengo el mismo sitio en un dominio .com y .org, no quiero enviar a alguien de .org a .com, especialmente si podrían tener tokens de inicio de sesión en .org que perderían si se envían al otro dominio ". - Este y algunos otros puntos interesantes de stackoverflow.com/questions/1459739/…
Yarin
55
@Yarin, no te olvides de verificar en la lista blanca los resultados deHTTP_HOST . De lo contrario, un atacante puede poner cualquier valor en la Host:solicitud de HTTP y hacer que el servidor lo acepte.
Pacerier
66
Principiantes: esta pregunta se refiere a valores típicamente obtenidos a través de $_SERVER['HTTP_HOST']o$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Respuestas:

780

Se HTTP_HOSTobtiene del encabezado de solicitud HTTP y esto es lo que el cliente realmente utilizó como "host de destino" de la solicitud. El SERVER_NAMEse define en la configuración del servidor. Cuál usar depende de para qué lo necesite. Sin embargo, ahora debe darse cuenta de que uno es un valor controlado por el cliente que, por lo tanto, puede no ser confiable para su uso en la lógica empresarial y el otro es un valor controlado por el servidor que es más confiable. Sin embargo, debe asegurarse de que el servidor web en cuestión SERVER_NAMEesté configurado correctamente. Tomando Apache HTTPD como ejemplo, aquí hay un extracto de su documentación :

Si no ServerNamese especifica, el servidor intenta deducir el nombre de host realizando una búsqueda inversa en la dirección IP. Si no se especifica ningún puerto en el ServerName, el servidor usará el puerto de la solicitud entrante. Para una confiabilidad y previsibilidad óptimas, debe especificar un nombre de host y un puerto explícitos utilizando la ServerNamedirectiva.


Actualización : después de verificar la respuesta de Pekka en su pregunta que contiene un enlace a la respuesta de bobince para la que PHP siempre devolvería HTTP_HOSTel valor SERVER_NAME, lo que va en contra de mi propia experiencia PHP 4.x + Apache HTTPD 1.2.x de hace un par de años , Soplé un poco de polvo de mi entorno XAMPP actual en Windows XP (Apache HTTPD 2.2.1 con PHP 5.2.8), lo inicié, creé una página PHP que imprime ambos valores, creé una aplicación de prueba Java URLConnectionpara modificar el Hostencabezado y las pruebas me enseñaron que este es (incorrectamente) el caso.

Después de sospechar por primera vez PHP y buscar algunos informes de errores de PHP con respecto al tema, aprendí que la raíz del problema está en el servidor web utilizado, que devolvió HTTP incorrectamenteHost encabezado cuando SERVER_NAMEse solicitó. Así que busqué informes de errores HTTPD de Apache usando varias palabras clave relacionadas con el tema y finalmente encontré un error relacionado . Este comportamiento se introdujo desde alrededor de Apache HTTPD 1.3. Es necesario configurar UseCanonicalNamela directiva a onla <VirtualHost>entrada de la ServerNameen httpd.conf(consultar también la advertencia en la parte inferior del documento !).

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

Esto funcionó para mí.

Resumido, SERVER_NAMEes más confiable, ¡pero usted depende de la configuración del servidor!

BalusC
fuente
55
De acuerdo, esto resuelve mi problema, que no está relacionado con el OP pero es relevante. Estaba muy preocupado por los problemas de seguridad al usar cualquier cosa que un navegador pudiera proporcionar. Esta respuesta fue una gran ayuda. Gracias por tomarse el tiempo para armarlo.
Yitzhak
2
¿Por qué dice que HTTP_HOST no es confiable? Sí, es proporcionado por el usuario, pero si el usuario le da un valor falso, la configuración de su servidor devolverá automáticamente 503 y su script PHP ni siquiera se ejecutará.
Pacerier
1
@Pacerier: al momento de escribir esta respuesta, no fue así. Las versiones se mencionan en la respuesta. Ya no sigo con PHP, así que no puedo decir si realmente ha cambiado en una versión más nueva.
BalusC
2
Una manera fácil de engañar a Apache desde WinXP es agregar una línea al archivo 'hosts' que indique que la IP del servidor está asignada a otro dominio, como este: "127.0.0.1 midominio.com". Lo he usado muchas veces para mostrar un sitio web local que engaña a mi audiencia para pensar que tengo una conexión a Internet y un sitio cargado muy rápido. Puede optar por el otro lado y engañar a Apache para que piense que se está ejecutando localmente, con "173.194.41.5 localhost", por lo que nunca debe confiar completamente en SERVER_NAME a menos que esté seguro de que Apache está bien configurado.
vicenteherrera
1
Solo quiero agregar que NGINX + PHP-FPM devuelve el valor establecido por la server_namedirectiva. Especialmente si no server_namese establece, también _SERVER["SERVER_NAME"]estará vacío.
white_gecko
69

HTTP_HOSTes el host de destino enviado por el cliente. Puede ser manipulado libremente por el usuario. No es problema enviar una solicitud a su sitio solicitando un HTTP_HOSTvalor de www.stackoverflow.com.

SERVER_NAMEproviene de la VirtualHostdefinición del servidor y, por lo tanto, se considera más confiable. Sin embargo, también puede manipularse desde el exterior en ciertas condiciones relacionadas con la configuración de su servidor web: consulte esta pregunta SO que trata los aspectos de seguridad de ambas variaciones.

No debe confiar en ninguno para estar a salvo. Dicho esto, qué usar realmente depende de lo que quieras hacer. Si desea determinar en qué dominio se está ejecutando su script, puede usarlo con seguridad HTTP_HOSTsiempre que los valores no válidos provenientes de un usuario malintencionado no puedan romper nada.

Pekka
fuente
8
Sí, pero la mayoría de los servidores HTTP rechazarían una solicitud que solicitara un valor HTTP_HOST de www.stackoverflow.com, por lo que el script PHP ni siquiera vería la solicitud.
Pacerier
2
@Pacerier verdadero, pero no siempre si el servidor no está configurado correctamente.
Pekka
1
Como se menciona en la publicación de BalusC, cuando accede a un servidor virtual Apache por IP, ambas variables contienen la IP (por defecto), no el nombre real del servidor. UseCanonicalName onDebe usar en httpd.conf para forzar SERVER_NAMEa ser el nombre real del servidor.
Simon East
@Pekka 웃, si el servidor no está configurado correctamente, $_SERVER['SERVER_NAME']no funcionaría tan bien . Un servidor mal configurado se establecerá en $_SERVER['SERVER_NAME']función del valor de la Host:solicitud del cliente . Ambos son iguales
Pacerier
Buena respuesta, pero no asumiría el alojamiento virtual.
Anthony Rutledge
55

Como mencioné en esta respuesta , si el servidor se ejecuta en un puerto que no sea 80 (como podría ser común en una máquina de desarrollo / intranet), entonces HTTP_HOSTcontiene el puerto, mientras SERVER_NAMEque no lo hace.

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

(Al menos eso es lo que he notado en los hosts virtuales basados ​​en el puerto de Apache)

Tenga en cuenta que HTTP_HOSTno no contiene:443 cuando se ejecuta en HTTPS (a no ser que se está ejecutando en un puerto no estándar, que no he probado).

Como otros han señalado, los dos también difieren cuando se usa IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Simon East
fuente
2
¿Cuándo arreglarán este comportamiento insidioso?
Pacerier
27

Tenga en cuenta que si quiere usar IPv6, probablemente quiera usarlo en HTTP_HOSTlugar de hacerlo SERVER_NAME. Si ingresa http://[::1]/las variables de entorno serán las siguientes:

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

Esto significa que si haces un mod_rewrite por ejemplo, podrías obtener un resultado desagradable. Ejemplo para una redirección 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}/

Esto aplica SOLO si accede al servidor sin un nombre de host.

Daniel Marschall
fuente
1
SiteGround, en su código https://%{SERVER_NAME}%{REQUEST_URI}
interno de
6

Si desea verificar a través de un server.php o lo que sea, desea llamarlo con lo siguiente:

<?php
    phpinfo(INFO_VARIABLES);
?>

o

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

    print_r($_SERVER);
?>

Luego acceda a él con todas las URL válidas para su sitio y compruebe la diferencia.

stevewh
fuente
5

Depende de lo que quiera averiguar. SERVER_NAME es el nombre de host del servidor, mientras que HTTP_HOST es el host virtual al que se conectó el cliente.

Rowland Shaw
fuente
44
No es exactamente cierto Rowland, SERVER_NAMEgeneralmente es el nombre del VirtualHost, no el servidor en sí. Y en Apache, a SERVER_NAMEmenudo se rellena con el mismo valor que HTTP_HOST(ver la respuesta de BalusC).
Simon East
1
@Simon, dado que los hosts de mosts ahora son VirtualHost, ¿qué quiere decir con el nombre del "servidor en sí"?
Pacerier
Si está ejecutando un servidor privado virtual (VPS) con un sitio web, no necesita suponer que se SERVER_NAMEaplica a un host virtual. Sin embargo, todavía se puede usar una configuración de host virtual para un sitio. Muchas personas usan hosting compartido, así que entiendo tu punto.
Anthony Rutledge
2

Me tomó un tiempo entender lo que la gente quería decir con " SERVER_NAMEes más confiable". Uso un servidor compartido y no tengo acceso a las directivas de host virtual. Por lo tanto, uso mod_rewrite en .htaccess para asignar diferentes correos electrónicos HTTP_HOSTa diferentes directorios. En ese caso, es HTTP_HOSTque tiene sentido.

La situación es similar si se usan hosts virtuales basados ​​en nombres: la ServerNamedirectiva dentro de un host virtual simplemente dice qué nombre de host se asignará a este host virtual. La conclusión es que, en ambos casos, el nombre de host proporcionado por el cliente durante la solicitud ( HTTP_HOST), debe coincidir con un nombre dentro del servidor, que se asigna a un directorio. Si el mapeo se realiza con directivas de host virtual o con reglas htaccess mod_rewrite es secundario aquí. En estos casos, HTTP_HOSTserá lo mismo que SERVER_NAME. Me alegra que Apache esté configurado de esa manera.

Sin embargo, la situación es diferente con los hosts virtuales basados ​​en IP. En este caso y solo en este caso, SERVER_NAMEy HTTP_HOSTpuede ser diferente, porque ahora el cliente selecciona el servidor por la IP, no por el nombre. De hecho, puede haber configuraciones especiales donde esto es importante.

Entonces, a partir de ahora, lo usaré SERVER_NAME, en caso de que mi código se transfiera a estas configuraciones especiales.

Dominic108
fuente
2

Suponiendo que uno tiene una configuración simple (CentOS 7, Apache 2.4.xy PHP 5.6.20) y solo un sitio web (sin asumir el alojamiento virtual) ...

En el sentido de PHP, $_SERVER['SERVER_NAME']es un elemento que PHP registra en el $_SERVERsuperglobal basado en su configuración de Apache ( **ServerName**directiva con UseCanonicalName On) en httpd.conf (ya sea desde un archivo de configuración de host virtual incluido, lo que sea, etc.). HTTP_HOST se deriva del HTTPhost encabezado . Tratar esto como entrada del usuario. Filtrar y validar antes de usar.

Aquí hay un ejemplo de dónde lo uso $_SERVER['SERVER_NAME']como base para una comparación. El siguiente método es de una clase secundaria concreta que hice nombrada ServerValidator(child of Validator). ServerValidatorcomprueba seis o siete elementos en $ _SERVER antes de usarlos.

Al determinar si la solicitud HTTP es POST, utilizo este método.

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

Para cuando se llame a este método, todo el filtrado y la validación de los elementos relevantes de $ _SERVER habrían ocurrido (y el conjunto de propiedades relevantes).

La línea ...

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

... comprueba que el $_SERVER['HTTP_HOST']valor (derivado en última instancia del hostencabezado HTTP solicitado ) coincida $_SERVER['SERVER_NAME'].

Ahora, estoy usando hablan superglobal para explicar mi ejemplo, pero eso es sólo porque algunas personas no están familiarizados con INPUT_GET, INPUT_POSTy INPUT_SERVERen lo que respecta a filter_input_array().

La conclusión es, yo no manejo peticiones POST en mi servidor a menos que todos se cumplan cuatro condiciones. Por lo tanto, en términos de solicitudes POST, el hecho de no proporcionar un hostencabezado HTTP (presencia probada para antes) significa fatalidad para los navegadores HTTP 1.0 estrictos . Por otra parte, el host solicitado debe coincidir con el valor de ServerNameen el httpd.conf , y, por extensión, el valor de $_SERVER('SERVER_NAME')la $_SERVERsuperglobal. Una vez más, estaría usando INPUT_SERVERlas funciones de filtro de PHP, pero me entiendes.

Tenga en cuenta que Apache usa con frecuencia ServerNameen redirecciones estándar (como dejar la barra diagonal fuera de una URL: por ejemplo, http://www.foo.com convirtiéndose en http://www.foo.com/ ), incluso si no está usando la reescritura de URL.

Yo uso $_SERVER['SERVER_NAME']como estándar, no $_SERVER['HTTP_HOST']. Hay mucho de ida y vuelta en este tema. $_SERVER['HTTP_HOST']podría estar vacío, por lo que esta no debería ser la base para crear convenciones de código como mi método público anterior. Pero, el hecho de que ambos se puedan configurar no garantiza que sean iguales. La prueba es la mejor manera de saberlo con certeza (teniendo en cuenta la versión de Apache y la versión de PHP).

Anthony Rutledge
fuente
0

Como dijo BalusC, SERVER_NAME no es confiable y se puede cambiar en la configuración de Apache, la configuración del nombre del servidor y el firewall que puede estar entre usted y el servidor.

La siguiente función siempre devuelve el host real (host escrito por el usuario) sin puerto y es casi confiable:

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

$ _SERVER ['SERVER_NAME'] se basa en la configuración de sus servidores web. $ _SERVER ['HTTP_HOST'] se basa en la solicitud del cliente.

Vitalie
fuente