request_uri - ¿Cuál es la diferencia entre HTTP_HOST y SERVER_NAME en PHP?
http_host example (9)
Como balusC dijo que SERVER_NAME no es confiable y puede cambiarse en apache config, configuración de nombre de servidor del servidor y 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;
}
¿Cuándo consideraría usar uno sobre el otro y por qué?
Como mencioné en esta respuesta , si el servidor se ejecuta en un puerto distinto de 80 (como puede ser común en una máquina de desarrollo / intranet), HTTP_HOST
contiene el puerto, mientras que SERVER_NAME
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 puertos de Apache)
Tenga en cuenta que HTTP_HOST
no contiene :443
cuando se ejecuta en HTTPS (a menos que se esté ejecutando en un puerto no estándar, que no he probado).
Como han señalado otros, los dos también difieren cuando se usa IPv6:
$_SERVER[''HTTP_HOST''] == ''[::1]''
$_SERVER[''SERVER_NAME''] == ''::1''
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.
El HTTP_HOST
se obtiene del encabezado de solicitud HTTP y esto es lo que el cliente realmente usó 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 de negocios 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 tenga el SERVER_NAME
configurado correctamente. Tomando Apache HTTPD como ejemplo, aquí hay un extracto de su documentación :
Si no se especifica ningún
ServerName
servidor, 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 enServerName
, entonces 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 directivaServerName
.
Actualización : después de verificar la respuesta de Pekka en su pregunta que contiene un enlace a la respuesta de Bobince, PHP siempre devolverá el valor de SERVER_NAME
para SERVER_NAME
, que va en contra de mis propias experiencias de PHP 4.x + Apache HTTPD 1.2.x de un par de Hace 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 los dos valores, creé una aplicación de prueba Java utilizando URLConnection
para modificar El encabezado de Host
y las pruebas me enseñaron que este es el caso (incorrectamente).
Después de sospechar por primera vez de PHP e investigar algunos informes de errores de PHP relacionados con el tema, me enteré de que la raíz del problema se encuentra en el servidor web utilizado, que devolvió incorrectamente el encabezado del Host
HTTP cuando se solicitó SERVER_NAME
. Así que busqué en los informes de errores de Apache HTTPD usando varias palabras clave relacionadas con el tema y finalmente encontré un error relacionado . Este comportamiento fue introducido desde alrededor de Apache HTTPD 1.3. UseCanonicalName
configurar la directiva UseCanonicalName
en la entrada <VirtualHost>
del ServerName
en httpd.conf
(¡también revise la advertencia en la parte inferior del UseCanonicalName !).
<VirtualHost *>
ServerName example.com
UseCanonicalName on
</VirtualHost>
Esto funcionó para mí.
Resumido, SERVER_NAME
es más confiable, ¡pero depende de la configuración del servidor!
Me tomó un tiempo entender lo que la gente quiere decir con '' SERVER_NAME
es más confiable''. Utilizo un servidor compartido y no tengo acceso a directivas de host virtual. Por lo tanto, uso mod_rewrite en .htaccess
para asignar diferentes HTTP_HOST
a diferentes directorios. En ese caso, es HTTP_HOST
que es significativo.
La situación es similar si uno usa hosts virtuales basados en nombres: la directiva ServerName
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 a su vez se asigna a un directorio. Si la asignación se realiza con directivas de host virtual o con las reglas htaccess mod_rewrite es secundaria aquí. En estos casos, HTTP_HOST
será el mismo que SERVER_NAME
. Me alegro de 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
pueden ser diferentes, porque ahora el cliente selecciona el servidor por IP, no por su nombre. De hecho, puede haber configuraciones especiales donde esto es importante.
Así que, a partir de ahora, SERVER_NAME
, en caso de que mi código sea portado en estas configuraciones especiales.
Si desea consultar a través de un server.php o como quiera 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 revise la diferencia.
Suponiendo que uno tiene una configuración simple (CentOS 7, Apache 2.4.x, y PHP 5.6.20) y solo un sitio web (no asumiendo el alojamiento virtual) ...
En el sentido de PHP, $_SERVER[''SERVER_NAME'']
es un elemento que PHP registra en el superglobal $_SERVER
basado en su configuración de Apache ( **ServerName**
directiva con UseCanonicalName On
) en httpd.conf (ya sea desde un host virtual incluido) archivo de configuración, lo que sea, etc ...). HTTP_HOST se deriva del encabezado de host
HTTP. Trate esto como entrada del usuario. Filtrar y validar antes de usar.
Aquí hay un ejemplo de donde uso $_SERVER[''SERVER_NAME'']
como base para una comparación. El siguiente método es de una clase secundaria concreta que hice llamada ServerValidator
(hijo de Validator
). ServerValidator
comprueba seis o siete elementos en $ _SERVER antes de usarlos.
Al determinar si la solicitud HTTP es POST, uso 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'')));
}
En el momento en que se llame a este método, todo el filtrado y la validación de los elementos $ _SERVER relevantes se habrían producido (y se habrían establecido las propiedades relevantes).
La línea ...
($this->httpHost === filter_input(INPUT_SERVER, ''SERVER_NAME'')
... comprueba que el $_SERVER[''HTTP_HOST'']
(derivado en última instancia del encabezado HTTP del host
solicitado) coincida con $_SERVER[''SERVER_NAME'']
.
Ahora, estoy usando el lenguaje superglobal para explicar mi ejemplo, pero eso se debe a que algunas personas no están familiarizadas con INPUT_GET
, INPUT_POST
e INPUT_SERVER
en lo que respecta a filter_input_array()
.
La conclusión es que no manejo las solicitudes POST en mi servidor a menos que se cumplan las cuatro condiciones. Por lo tanto, en términos de solicitudes POST, el hecho de no proporcionar un encabezado de host
HTTP (presencia probada anteriormente) deletrea la perdición de los navegadores HTTP 1.0 estrictos. Además, el host solicitado debe coincidir con el valor de ServerName
en httpd.conf y, por extensión, el valor para $_SERVER(''SERVER_NAME'')
en el $_SERVER
superglobal. Una vez más, estaría usando INPUT_SERVER
con las funciones de filtro de PHP, pero atrapa mi deriva.
Tenga en cuenta que Apache usa con frecuencia el nombre del ServerName
en redirecciones estándar (como dejar la barra inclinada fuera de una URL: Ejemplo, http://www.foo.com/ convierte en http://www.foo.com/ ), incluso si está No se utiliza la reescritura de URL.
Utilizo $_SERVER[''SERVER_NAME'']
como estándar, no $_SERVER[''HTTP_HOST'']
. Hay un montón 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, solo porque ambos pueden ser establecidos no garantiza que serán iguales. La prueba es la mejor manera de saberlo con seguridad (teniendo en cuenta la versión de Apache y la versión de PHP).
Tenga en cuenta que si desea utilizar IPv6, probablemente desee utilizar HTTP_HOST
lugar de SERVER_NAME
. Si ingresa http://[::1]/
las variables de entorno serán las siguientes:
HTTP_HOST = [::1]
SERVER_NAME = ::1
Esto significa que si hace un mod_rewrite por ejemplo, puede 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 se aplica SOLAMENTE si accede al servidor sin un nombre de host.
HTTP_HOST
es el host de destino enviado por el cliente. Puede ser manipulado libremente por el usuario. No es un problema enviar una solicitud a su sitio solicitando un valor www..com
de www..com
.
SERVER_NAME
proviene de la definición de VirtualHost
del servidor y, por lo tanto, se considera más confiable. Sin embargo, también puede manipularse desde el exterior bajo ciertas condiciones relacionadas con la configuración de su servidor web: vea esto Esta pregunta de SO que trata sobre los aspectos de seguridad de ambas variaciones.
No debes confiar en ninguno de los dos para estar seguro. Dicho esto, lo que se debe usar realmente depende de lo que se quiera hacer. Si desea determinar en qué dominio se está ejecutando su script, puede usar HTTP_HOST
forma segura siempre que los valores no válidos provenientes de un usuario malintencionado no puedan romper nada.