Discusión en profundidad de concordancia de host virtuales

El código de host virtuales ha sido completamente reescrito en Apache 1.3. Este documento intenta explicar qué hace exactamente Apache cuando debe decidir desde qué host virtual debe servir una petición. Con la ayuda de la nueva directiva NameVirtualHost la configuración de un host virtual es algo más fácil y seguro que con las versiones anteriores a la 1.3.

Si únicamente quiere hacerlo funcionar sin entender cómo, aquí tiene algunos ejemplos.

Comprobación del fichero de configuración

Existe un servidor principal que consiste en todas las definiciones que están fuera de las secciones llamadas <VirtualHost>. Existen los servidores virtuales, llamados vhosts, que están definidos dentro de las secciones <VirtualHost>.

Las directivas Port, ServerName, ServerPath, y ServerAlias pueden aparecer en cualquier parte dentro de la definición de un servidor. Sin embargo, cada vez que aparecen sobreescriben el valor anterior (dentro de ese servidor).

El valor por defecto del campo Port para el servidor principal es 80. El servidor principal no tiene una valor por defecto para ServerPath o para ServerAlias. El valor por defecto de ServerName se obtiene de la dirección IP de los servidores.

La directiva Port del servidor principal tiene dos funciones mantenidas por compatibilidad con los ficheros de configuración de NCSA. Una de las funciones es para determinar el puerto de red por defecto al que Apache escuchará. Este valor se sobreescribe con el valor de las directivas Listen. La segunda función es para especificar el número de puerto que se usa en URIs absolutas durante las redirecciones.

A diferencia del servidor principal, los puertos del host virtual NO afectan a los puertos que Apache atiende en las conexiones.

Cada dirección que aparece en la directiva VirtualHost puede tener un puerto opcional. Si no se especifica, toma el valor de la declaración Port más reciente. El puerto especial * indica cualquier puerto. El conjunto completo de direcciones (incluyendo múltiples registros de tipo A búsquedas DNS) se llaman conjunto de direcciones del host virtual.

A menos que se especifique una directiva NameVirtualHost para una dirección IP específica, el primer host virtual con esa dirección es tratado como un host virtual basado en IP. La versión 1.3.13 de Apache y posteriores la dirección IP *.

Si se utiliza host virtuales basados en nombre, se debe poner la directiva NameVirtualHost con la dirección IP para ese host virtual. En otras palabras; debe especificar por medio de la directiva NameVirtualHost la dirección IP que tengan los alias de host (los registros CNAMEs) de sus host virtuales basados en nombre.

Se pueden utilizar múltiples directivas NameVirtualHost con un conjunto de directivas VirtualHost, pero sólo una directiva NameVirtualHost debería ser usada para cada para IP:puerto.

El orden de las directivas NameVirtualHost y VirtualHost no es importante, lo quq hace que los siguientes ejemplos sean idénticos (solamente el orden de las directivas VirtualHost para un conjunto de direcciones es importante. Véalo abajo):

                                |
  NameVirtualHost 111.22.33.44  | <VirtualHost 111.22.33.44>
  <VirtualHost 111.22.33.44>    | # servidor A
  # servidor A 		        | </VirtualHost>
  ... 			        | <VirtualHost 111.22.33.55>
  </VirtualHost>	        | # servidor C
  <VirtualHost 111.22.33.44>    | ...
  # servidor B 		        | </VirtualHost>
  ... 			        | <VirtualHost 111.22.33.44>
  </VirtualHost>	        | # servidor B
                                | ...
  NameVirtualHost 111.22.33.55  | </VirtualHost>
  <VirtualHost 111.22.33.55>    | <VirtualHost 111.22.33.55>
  # servidor C 		        | # servidor D
  ... 			        | ...
  </VirtualHost>	        | </VirtualHost>
  <VirtualHost 111.22.33.55>    |
  # servidor D 		        | NameVirtualHost 111.22.33.44
  ... 			        | NameVirtualHost 111.22.33.55
  </VirtualHost>	        |
                                |

(Para mejorar la legibilidad de su fichero de configuración quizás prefiera la variante de la izquierda).

Después de comprobar la directiva VirtualHost, al servidor virtual se le asigna un puerto igual al que se asignó al primer nombre en su directiva VirtualHost.

La lista completa de nombre en la directiva VirtualHost es tratada como un ServerAlias (pero su valor no se sobreescribe con ninguna declaración ServerAlias) siempre que todos los nombres se resuelvan en el mismo conjugo de direccionest. Tenga en cuenta que las declaraciones Port subsiguientes para este host virtual no afectarán a los puertos asignados en el conjunto de direcciones.

Durante la inicialización se genera e inserta en una tabla hash una lista para cada dirección IP. Si la dirección se utiliza en una directiva NameVirtualHost, la lista contendrá todos los host virtuales basados en nombre para esa dirección IP. Si no se definió ningún host virtual, la directiva NameVirtualHost es ignorada y se guarda un error en el fichero de logs. Si se da el caso de que se utiliza host virtual basado en IP, la tabla hash permanece vacía.

Gracias a una rápida función hash, el hecho de sobrecargar una tabla hash con direcciones IP durante una petición es mínima o incluso nula. Además, la tabla está optimizada para direcciones IP que varían en su último octeto.

Para cada host virtual se fijan varios valores por defecto, particularmente:

  1. Si un host virtual no tiene las directivas ServerAdmin, ResourceConfig, AccessConfig, Timeout, KeepAliveTimeout, KeepAlive, MaxKeepAliveRequests, o SendBufferSize, entonces el valor respectivo se toma del servidor principal. Es decir, se hereda el último valor que tuviera en el servidor principal, cualquiera que sea.
  2. La "la búsqueda de valores por defecto" que define los permisos de directorio por defecto para el host virtual se entremezclan con los que hubiera en el servidor principal. Esto incluye cualquier configuración por cada directorio para cualquier módulo.
  3. Las configuraciones por servidor para cada módulo tomados del servidor principal se mezclan con el host virtual.
Básicamente, el servidor principal es tratado como "por defecto" o como "base" sobre el que se levanta cada host virtual. Sin embargo, la posición de las definiciones en el fichero de configuración del servidor principal es totalmente irrelevante; el fichero de configuración es "parseado" (chequeado) una vez que la "mezcla" de valores se haya llevado a cabo. Así que, si una definición en el servidor principal aparece después de una definición en el host virtual, podría afectar a la definición del host virtual.

Si el principal no tiene ningún ServerName, se usará el nombre de host de la máquina sobre la que httpd esté corriendo. Llamaremos a las direcciones devueltas por una búsqueda en DNS que aparecen en ServerName del servidor principal conjunto de direcciónes del servidor principal.

Para cualquier campo ServerName no definido, el host virtual basado en nombre toma como valor por la primera dirección proporcionada por la declaración VirtualHost que define el host virtual.

A cualquier host virtual que incluya _default_ se le da el mismo ServerName que el servidor principal.

Concordancia de host virtual

Para una petición dada, el servidor determina qué host virtual usar de este modo:

Búsqueda en una tabla hash

Cuando el cliente realiza la primera conexión, la dirección IP a la que el cliente conectó se busca en la tabla hash interna.

Si la búsqueda no tiene éxito (no se encontró la la dirección IP), la petición se sirve desde el host virtual _default_, siempre que éste exista para el puerto al que el cliente envió la petición. Si esto último tampoco ha tenido éxito, la petición se sirve desde el servidor principal.

En Apache 1.3.13 y versiones posteriores, si la dirección IP no se encuentra en la tabla hash, pero sí concuerda el puerto, puede dar como resultado una entrada correspondiente a un NameVirtualHost *, que es entonces manejado como cualquier otro host virtual basado en nombre.

Si la búsqueda tuvo éxito (se encontró una entrada para la dirección IP), el siguiente paso consiste en decidir si hay que tratar con un host virtual basado en IP o basado en nombre.

Host virtual basado en nombre

Si la entrada que se ha encontrado tiene una lista de nombres vacía, entonces significa que tenemos un host virtual basado en IP, por lo que no se realiza ninguna otra acción y la petición se sirve desde ese host virtual.

Host virtual basado en nombre

Si la entrada corresponde a un host virtual basado en nombre, la lista de nombres contendrá una o más estructuras de host virtuales. Esta lista contiene los host virtuales en el mismo orden en que aparecen en las directivas VirtualHost del fichero de configuración.

El primer host virtual de esta lista (el primer host virtual en el fichero de configuración con la dirección IP especificada) tiene la más alta prioridad y se "apodera" de cualquier petición a un servidor desconocido o de cualquier petición sin el campo de cabecera Host:.

Si el cliente proporciona un campo de cabecera Host:, entonces se busca en la lista una concordancia de un host virtual y se toma la primera ocurrencia en ServerName o ServerAlias, siendo la petición servida desde ese host virtual. Un campo de cabecera Host: puede contener un número de puerto, pero Apache siempre busca la concordancia contra el puerto real al que el cliente envió la petición.

Si el cliente envió una petición HTTP/1.0 sin el campo Host:, no sabríamos a qué servidor intenta conectar, por lo que cualquier ServerPath concordará con el URI de la petición. De este modo, la primera concordancia en la lista será la usada y la petición será servida desde ese host virtual.

Si no se pudo encontrar ningún host virtual que concuerde, la petición será servida por el primer host virtual cuyo puerto coincida en la lista con la IP a la que el cliente conectó, tal y como se mecionó antes.

Conexiones persistentes

El modo de búsqueda descrito arriba se realiza solamente una vez por cada sesión TCP/IP, mientras que la búsqueda de nombre se realiza por cada petición en una conexión persistente. En otras palabras, un cliente puede pedir páginas desde diferentes host virtuales basados en nombre durante una única conexión persistente.

URI absoluto

Si el URI de una petición es absoluto y su nombre de host y puerto coiciden con el el servidor principal o uno de los host virtuales configurados y además coincide con la dirección y puerto al que el cliente envió la petición, entonces se quita el prefijo esquema/nombre host/puerto y el URI restante se sirve por el correspondiente servidor principal o host virtual. Si no concuerda, el URI permanece intacto y la petición se considera como una petición de proxy.

Observaciones

Recomendaciones

Además de las recomendaciones sobre cuestiones relacionadas con DNS, aquí tiene algunas otras: