Virtual Hosts en Apache para múltiples sitios

Capítulo 5

Tiempo estimado de lectura: 7 minutos

+ Ejercicio

Qué es un Virtual Host (vhost) basado en nombre

Un Virtual Host basado en nombre permite servir múltiples sitios web desde la misma IP/puerto (por ejemplo, *:80), diferenciándolos por el nombre de dominio que el cliente solicita. Apache decide qué sitio entregar leyendo el encabezado HTTP Host y comparándolo con la configuración de los bloques <VirtualHost>.

Flujo de resolución: DNS/hosts → Host header → selección de vhost

  • DNS o /etc/hosts: el nombre (p. ej. site1.local) se resuelve a una IP (p. ej. 127.0.0.1 o la IP del servidor).
  • Cliente envía HTTP: al conectarse a esa IP, el navegador/curl envía el encabezado Host: site1.local.
  • Apache hace matching: busca un <VirtualHost IP:PUERTO> que coincida con la IP/puerto de la conexión y, dentro de esos, el que coincida con ServerName/ServerAlias. Si no hay coincidencia, usa el vhost por defecto (normalmente el primero cargado para ese IP:puerto).

Estructura del bloque <VirtualHost>

Un vhost típico incluye: identidad del sitio (ServerName/ServerAlias), raíz de documentos (DocumentRoot), permisos/directivas por directorio y logs separados para facilitar diagnóstico.

<VirtualHost *:80>    ServerName ejemplo.local    ServerAlias www.ejemplo.local    DocumentRoot /var/www/ejemplo/public    ErrorLog ${APACHE_LOG_DIR}/ejemplo_error.log    CustomLog ${APACHE_LOG_DIR}/ejemplo_access.log combined    <Directory /var/www/ejemplo/public>        Require all granted    </Directory></VirtualHost>

ServerName y ServerAlias

  • ServerName: nombre principal del sitio (debe ser único por IP:puerto).
  • ServerAlias: nombres alternativos que deben servir el mismo sitio (p. ej. www, subdominios, etc.).

DocumentRoot por sitio

Cada vhost debe apuntar a su propio directorio de contenido. Esto evita mezclar archivos y permite despliegues independientes. Mantén una estructura consistente, por ejemplo:

/var/www/site1/public/ /var/www/site2/public/

Logs separados por sitio

Separar logs por vhost acelera el troubleshooting. Usa ErrorLog y CustomLog con nombres distintos por sitio. En Debian/Ubuntu suele existir ${APACHE_LOG_DIR} (normalmente /var/log/apache2).

Práctica guiada: dos sitios en el mismo servidor (name-based)

Objetivo: servir site1.local y site2.local desde el mismo Apache en el puerto 80, con DocumentRoot y logs separados, simulando DNS con /etc/hosts y validando con curl.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

Paso 1: crear directorios y contenido mínimo

sudo mkdir -p /var/www/site1/public sudo mkdir -p /var/www/site2/public
echo 'SITE 1 OK' | sudo tee /var/www/site1/public/index.html echo 'SITE 2 OK' | sudo tee /var/www/site2/public/index.html

Verifica permisos/propietario según tu política del sistema (lo importante es que Apache pueda leer esos archivos).

Paso 2: crear los archivos de sitio (vhosts)

En Debian/Ubuntu, lo habitual es crear archivos en /etc/apache2/sites-available/. Crea dos archivos: site1.conf y site2.conf.

sudo nano /etc/apache2/sites-available/site1.conf
<VirtualHost *:80>    ServerName site1.local    ServerAlias www.site1.local    DocumentRoot /var/www/site1/public    ErrorLog ${APACHE_LOG_DIR}/site1_error.log    CustomLog ${APACHE_LOG_DIR}/site1_access.log combined    <Directory /var/www/site1/public>        Require all granted    </Directory></VirtualHost>
sudo nano /etc/apache2/sites-available/site2.conf
<VirtualHost *:80>    ServerName site2.local    ServerAlias www.site2.local    DocumentRoot /var/www/site2/public    ErrorLog ${APACHE_LOG_DIR}/site2_error.log    CustomLog ${APACHE_LOG_DIR}/site2_access.log combined    <Directory /var/www/site2/public>        Require all granted    </Directory></VirtualHost>

Nota: si tu Apache está escuchando en otra IP/puerto, ajusta *:80 a la combinación correcta (por ejemplo, 192.168.1.10:80).

Paso 3: habilitar sitios y deshabilitar el sitio por defecto

En Debian/Ubuntu, se usan a2ensite y a2dissite para gestionar enlaces en sites-enabled.

sudo a2ensite site1.conf sudo a2ensite site2.conf

Si existe el sitio por defecto (000-default.conf) y no lo quieres, desactívalo:

sudo a2dissite 000-default.conf

Paso 4: validar sintaxis y recargar sin cortar conexiones

Antes de aplicar cambios, valida la configuración:

sudo apachectl configtest

Para aplicar cambios sin cortar conexiones, usa reload (recarga graciosa):

sudo systemctl reload apache2

Alternativa equivalente (según distro):

sudo apachectl graceful

Diferencia práctica: reload/graceful recarga configuración manteniendo procesos/hilos atendiendo conexiones existentes; restart reinicia el servicio y puede interrumpir conexiones.

Paso 5: simular DNS con /etc/hosts

En tu máquina cliente (o en el mismo servidor si pruebas local), agrega entradas a /etc/hosts apuntando a la IP del servidor. Si pruebas en la misma máquina, puedes usar 127.0.0.1.

sudo nano /etc/hosts
127.0.0.1 site1.local 127.0.0.1 site2.local

Paso 6: validar con curl (incluyendo -H 'Host:')

Prueba por nombre (si /etc/hosts ya resuelve):

curl -i http://site1.local/ curl -i http://site2.local/

Prueba forzando el encabezado Host (útil si apuntas a una IP directamente o si quieres verificar el matching):

curl -i http://127.0.0.1/ -H 'Host: site1.local' curl -i http://127.0.0.1/ -H 'Host: site2.local'

Debes ver en el cuerpo SITE 1 OK o SITE 2 OK según el Host enviado.

Cómo Apache decide el vhost: reglas prácticas de matching

1) Coincidencia por IP:puerto del bloque <VirtualHost>

Apache primero agrupa vhosts por el par IP:PUERTO (por ejemplo, todos los *:80). Si tu conexión entra por :80, solo compiten los vhosts definidos para :80.

2) Coincidencia por ServerName/ServerAlias vs Host header

Dentro del grupo, compara el valor del encabezado Host con ServerName y ServerAlias. Si coincide, ese vhost se usa para servir la petición.

3) Sitio por defecto (default vhost)

Si no hay coincidencia por nombre, Apache usa el primer vhost cargado para ese IP:PUERTO. Por eso el orden de carga importa.

Detectar cuál es el sitio por defecto y el orden de carga

Para ver qué vhosts están activos y cuál actúa como default, usa:

sudo apachectl -S

Este comando muestra los vhosts por puerto, el archivo donde se definen y marca el default para cada IP:PUERTO. Si al pedir un dominio inexistente te responde un sitio “equivocado”, normalmente estás cayendo en el vhost por defecto.

Procedimientos de administración: habilitar, deshabilitar y aplicar cambios

Habilitar un sitio

sudo a2ensite site1.conf sudo apachectl configtest sudo systemctl reload apache2

Deshabilitar un sitio

sudo a2dissite site2.conf sudo apachectl configtest sudo systemctl reload apache2

Errores comunes al habilitar/deshabilitar

  • Olvidar recargar: habilitar/deshabilitar no aplica hasta reload.
  • Conflicto de ServerName: dos vhosts con el mismo ServerName en el mismo IP:PUERTO generan comportamiento inesperado.
  • DocumentRoot incorrecto: si apunta a un directorio inexistente, verás errores en el ErrorLog del vhost (si está bien separado) o en el log general.

Verificación con logs separados (diagnóstico rápido)

Tras hacer peticiones con curl, revisa accesos y errores por sitio:

sudo tail -n 50 /var/log/apache2/site1_access.log sudo tail -n 50 /var/log/apache2/site1_error.log sudo tail -n 50 /var/log/apache2/site2_access.log sudo tail -n 50 /var/log/apache2/site2_error.log

Si envías Host: site1.local y el acceso aparece en site2_access.log, el matching no está funcionando como esperas (revisa ServerName/ServerAlias, el puerto, y el orden/default con apachectl -S).

Ejercicios prácticos recomendados

Ejercicio A: comprobar el vhost por defecto

  • Deja habilitados site1 y site2.
  • Haz una petición con un host inexistente:
curl -i http://127.0.0.1/ -H 'Host: noexiste.local'
  • Identifica qué sitio respondió y confirma con sudo apachectl -S cuál es el default para *:80.

Ejercicio B: probar ServerAlias

  • Agrega en /etc/hosts:
127.0.0.1 www.site1.local
  • Valida:
curl -i http://127.0.0.1/ -H 'Host: www.site1.local'

Ejercicio C: deshabilitar un sitio y observar el fallback

  • Deshabilita site2:
sudo a2dissite site2.conf sudo apachectl configtest sudo systemctl reload apache2
  • Vuelve a pedir site2.local:
curl -i http://127.0.0.1/ -H 'Host: site2.local'
  • Observa qué sitio responde ahora (probablemente el default) y verifica el motivo con apachectl -S.

Ahora responde el ejercicio sobre el contenido:

En un entorno con Virtual Hosts basados en nombre en el mismo IP:puerto, ¿qué sucede si Apache recibe una petición cuyo encabezado Host no coincide con ningún ServerName ni ServerAlias configurado para ese IP:puerto?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

Si el Host no coincide con ningún ServerName/ServerAlias dentro del grupo de vhosts del mismo IP:PUERTO, Apache usa el vhost por defecto, que suele ser el primero cargado para esa combinación.

Siguiente capítulo

Registro y diagnóstico en Apache: access log, error log y trazabilidad

Arrow Right Icon
Portada de libro electrónico gratuitaApache desde Cero: Guía Práctica para Principiantes
50%

Apache desde Cero: Guía Práctica para Principiantes

Nuevo curso

10 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.