¿Cuál es la diferencia entre HTTP_HOST
y SERVER_NAME
en 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é?
fuente
¿Cuál es la diferencia entre HTTP_HOST
y SERVER_NAME
en 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é?
Se HTTP_HOST
obtiene del encabezado de solicitud HTTP y esto es lo que el cliente realmente utilizó como "host de destino" de la solicitud. El SERVER_NAME
se 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_NAME
esté configurado correctamente. Tomando Apache HTTPD como ejemplo, aquí hay un extracto de su documentación :
Si no
ServerName
se 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 elServerName
, 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 laServerName
directiva.
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_HOST
el 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 URLConnection
para modificar el Host
encabezado 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_NAME
se 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 UseCanonicalName
la directiva a on
la <VirtualHost>
entrada de la ServerName
en 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_NAME
es más confiable, ¡pero usted depende de la configuración del servidor!
server_name
directiva. Especialmente si no server_name
se establece, también _SERVER["SERVER_NAME"]
estará vacío.
HTTP_HOST
es 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_HOST
valor de www.stackoverflow.com
.
SERVER_NAME
proviene de la VirtualHost
definició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_HOST
siempre que los valores no válidos provenientes de un usuario malintencionado no puedan romper nada.
UseCanonicalName on
Debe usar en httpd.conf para forzar SERVER_NAME
a ser el nombre real del servidor.
$_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
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_HOST
contiene el puerto, mientras SERVER_NAME
que 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_HOST
no 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'
Tenga en cuenta que si quiere usar IPv6, probablemente quiera usarlo en HTTP_HOST
lugar 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.
https://%{SERVER_NAME}%{REQUEST_URI}
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.
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.
SERVER_NAME
generalmente es el nombre del VirtualHost, no el servidor en sí. Y en Apache, a SERVER_NAME
menudo se rellena con el mismo valor que HTTP_HOST
(ver la respuesta de BalusC).
SERVER_NAME
aplica 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.
Me tomó un tiempo entender lo que la gente quería decir con " SERVER_NAME
es 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_HOST
a diferentes directorios. En ese caso, es HTTP_HOST
que tiene sentido.
La situación es similar si se usan hosts virtuales basados en nombres: la ServerName
directiva 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_HOST
será 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_NAME
y HTTP_HOST
puede 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.
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 $_SERVER
superglobal 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
). ServerValidator
comprueba 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 host
encabezado 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_POST
y INPUT_SERVER
en 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 host
encabezado 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 ServerName
en el httpd.conf , y, por extensión, el valor de $_SERVER('SERVER_NAME')
la $_SERVER
superglobal. Una vez más, estaría usando INPUT_SERVER
las funciones de filtro de PHP, pero me entiendes.
Tenga en cuenta que Apache usa con frecuencia ServerName
en 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).
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;
}
$ _SERVER ['SERVER_NAME'] se basa en la configuración de sus servidores web. $ _SERVER ['HTTP_HOST'] se basa en la solicitud del cliente.
HTTP_HOST
. De lo contrario, un atacante puede poner cualquier valor en laHost:
solicitud de HTTP y hacer que el servidor lo acepte.$_SERVER['HTTP_HOST']
o$_SERVER['SERVER_NAME']