Configuración de Virtual Hosts en Apache: Guía Completa

Introducción

El alojamiento virtual es una potente característica de Apache que permite que un único servidor físico aloje múltiples sitios web, cada uno con su propio nombre de dominio, configuración y contenido. Esta capacidad ha revolucionado el alojamiento web, haciendo rentable ejecutar numerosos sitios web en un solo servidor mientras se mantiene un aislamiento completo entre sitios.

Apache admite dos tipos de alojamiento virtual: basado en nombres y basado en IP. El alojamiento virtual basado en nombres, el enfoque más común, utiliza la cabecera HTTP Host para determinar qué sitio web servir, permitiendo sitios web ilimitados en una sola dirección IP. El alojamiento virtual basado en IP asigna a cada sitio web una dirección IP única, lo cual es necesario para ciertas configuraciones SSL/TLS o cuando el alojamiento basado en nombres no es adecuado.

Esta guía completa cubre todo lo que necesitas saber sobre la configuración de virtual hosts en Apache. Aprenderás cómo configurar virtual hosts basados tanto en nombres como en IP, configurar certificados SSL/TLS, implementar las mejores prácticas de seguridad, solucionar problemas comunes y optimizar el rendimiento. Ya sea que estés alojando un pequeño blog o administrando un entorno complejo con múltiples sitios, dominar los virtual hosts es esencial para una administración efectiva de Apache.

Requisitos Previos

Antes de configurar virtual hosts, asegúrate de tener:

  • Apache instalado y ejecutándose en Ubuntu/Debian o CentOS/Rocky Linux
  • Acceso root o sudo a tu servidor
  • Comprensión básica de la configuración de Apache
  • Nombres de dominio configurados con DNS apuntando a la IP de tu servidor
  • Familiaridad con los permisos de archivos y la estructura de directorios de Linux
  • Conocimiento de editores de texto (nano, vim u otros)
  • Comprensión de conceptos básicos de redes
  • Certificados SSL/TLS (si configuras virtual hosts HTTPS)

Comprendiendo los Virtual Hosts

Virtual Hosts Basados en Nombres

El alojamiento virtual basado en nombres permite múltiples dominios en una dirección IP:

Flujo de Solicitud del Cliente:
1. El cliente solicita http://example.com/page.html
2. El navegador envía una solicitud HTTP con cabecera "Host: example.com"
3. Apache recibe la solicitud en la dirección IP 192.168.1.100:80
4. Apache verifica la cabecera Host contra los virtual hosts configurados
5. Apache sirve contenido del virtual host coincidente
6. Si no encuentra coincidencia, sirve del virtual host predeterminado

Ejemplo de configuración:

# Todos los virtual hosts comparten la misma IP
<VirtualHost 192.168.1.100:80>
    ServerName example.com
    DocumentRoot /var/www/example.com
</VirtualHost>

<VirtualHost 192.168.1.100:80>
    ServerName another.com
    DocumentRoot /var/www/another.com
</VirtualHost>

Virtual Hosts Basados en IP

El alojamiento virtual basado en IP asigna a cada sitio una IP única:

# Cada virtual host tiene una IP diferente
<VirtualHost 192.168.1.100:80>
    ServerName example.com
    DocumentRoot /var/www/example.com
</VirtualHost>

<VirtualHost 192.168.1.101:80>
    ServerName another.com
    DocumentRoot /var/www/another.com
</VirtualHost>

Casos de uso:

  • SSL/TLS con Server Name Indication (SNI) no disponible
  • Clientes que no envían cabeceras Host
  • Requisito de direcciones IP separadas por sitio

Estructura de Directorios de Virtual Hosts

Ubuntu y Debian

Apache en Ubuntu/Debian usa una estructura sites-available/sites-enabled:

/etc/apache2/
├── sites-available/          # Todas las configuraciones de virtual hosts
│   ├── 000-default.conf     # Virtual host HTTP predeterminado
│   ├── default-ssl.conf     # Virtual host HTTPS predeterminado
│   ├── example.com.conf     # Virtual host personalizado
│   └── another.com.conf     # Otro virtual host
└── sites-enabled/            # Virtual hosts habilitados (enlaces simbólicos)
    ├── 000-default.conf -> ../sites-available/000-default.conf
    └── example.com.conf -> ../sites-available/example.com.conf

Flujo de trabajo:

  1. Crear configuración en sites-available/
  2. Habilitar con el comando a2ensite (crea enlace simbólico)
  3. Recargar Apache para aplicar cambios

CentOS, Rocky Linux y AlmaLinux

Los sistemas basados en Red Hat usan un enfoque más simple:

/etc/httpd/
└── conf.d/                   # Todos los archivos *.conf se cargan
    ├── example.com.conf
    ├── another.com.conf
    └── ssl.conf

Flujo de trabajo:

  1. Crear archivo .conf en conf.d/
  2. Recargar Apache para aplicar cambios

Creando tu Primer Virtual Host

Paso 1: Crear el Directorio Raíz de Documentos

Crea la estructura de directorios para tu sitio web:

# Ubuntu/Debian
sudo mkdir -p /var/www/example.com/public_html
sudo mkdir -p /var/www/example.com/logs

# CentOS/Rocky/AlmaLinux
sudo mkdir -p /var/www/example.com/public_html
sudo mkdir -p /var/www/example.com/logs

Paso 2: Establecer Permisos Apropiados

Configurar propiedad y permisos:

# Ubuntu/Debian
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com

# CentOS/Rocky/AlmaLinux
sudo chown -R apache:apache /var/www/example.com
sudo chmod -R 755 /var/www/example.com

En sistemas con SELinux habilitado (CentOS/Rocky/AlmaLinux):

# Establecer contexto SELinux correcto
sudo chcon -R -t httpd_sys_content_t /var/www/example.com/public_html
sudo chcon -R -t httpd_log_t /var/www/example.com/logs

# Hacerlo permanente
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/example.com/public_html(/.*)?"
sudo semanage fcontext -a -t httpd_log_t "/var/www/example.com/logs(/.*)?"
sudo restorecon -Rv /var/www/example.com

Paso 3: Crear Contenido de Prueba

Crear un archivo index.html simple:

sudo nano /var/www/example.com/public_html/index.html

Agregar el siguiente contenido:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bienvenido a Example.com</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background: white;
            padding: 40px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        h1 {
            color: #d14836;
            border-bottom: 3px solid #d14836;
            padding-bottom: 10px;
        }
        .info {
            background: #e8f4f8;
            padding: 15px;
            border-left: 4px solid #0088cc;
            margin: 20px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Bienvenido a Example.com</h1>
        <p>¡Tu virtual host de Apache está configurado correctamente!</p>
        <div class="info">
            <strong>Información del Servidor:</strong><br>
            Virtual Host: example.com<br>
            Raíz de Documentos: /var/www/example.com/public_html<br>
            Estado: Activo y Ejecutándose
        </div>
        <p>Ahora puedes subir el contenido de tu sitio web a este directorio.</p>
    </div>
</body>
</html>

Paso 4: Crear la Configuración del Virtual Host

Configuración en Ubuntu/Debian

Crear el archivo de configuración del virtual host:

sudo nano /etc/apache2/sites-available/example.com.conf

Agregar la configuración básica:

<VirtualHost *:80>
    # Configuración básica
    ServerName example.com
    ServerAlias www.example.com
    ServerAdmin [email protected]

    # Raíz de documentos
    DocumentRoot /var/www/example.com/public_html

    # Permisos de directorio
    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # Registro de logs
    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined

    # Opcional: Establecer variable de entorno
    SetEnv ENVIRONMENT production
</VirtualHost>

Habilitar el virtual host:

# Habilitar el sitio
sudo a2ensite example.com.conf

# Probar la configuración
sudo apache2ctl configtest

# Recargar Apache
sudo systemctl reload apache2

Configuración en CentOS/Rocky/AlmaLinux

Crear el archivo de configuración:

sudo nano /etc/httpd/conf.d/example.com.conf

Agregar la configuración:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    ServerAdmin [email protected]

    DocumentRoot /var/www/example.com/public_html

    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined
</VirtualHost>

Probar y recargar:

# Probar la configuración
sudo httpd -t

# Recargar Apache
sudo systemctl reload httpd

Configuración Avanzada de Virtual Hosts

Múltiples Nombres de Dominio (ServerAlias)

Alojar múltiples dominios apuntando al mismo contenido:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com example.net www.example.net example.org
    DocumentRoot /var/www/example.com/public_html

    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/example-error.log
    CustomLog ${APACHE_LOG_DIR}/example-access.log combined
</VirtualHost>

ServerAlias con Comodines

Coincidir con todos los subdominios:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias *.example.com
    DocumentRoot /var/www/example.com/public_html

    # Registrar el subdominio en los logs de acceso
    LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_combined
    CustomLog ${APACHE_LOG_DIR}/example-access.log vhost_combined
</VirtualHost>

Virtual Hosts de Subdominios

Crear virtual hosts separados para subdominios:

# Dominio principal
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public_html
</VirtualHost>

# Subdominio de blog
<VirtualHost *:80>
    ServerName blog.example.com
    DocumentRoot /var/www/example.com/blog

    <Directory /var/www/example.com/blog>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/blog.example-error.log
    CustomLog ${APACHE_LOG_DIR}/blog.example-access.log combined
</VirtualHost>

# Subdominio de API
<VirtualHost *:80>
    ServerName api.example.com
    DocumentRoot /var/www/example.com/api

    <Directory /var/www/example.com/api>
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    # Formato de registro específico para API
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" api_log
    CustomLog ${APACHE_LOG_DIR}/api.example-access.log api_log
</VirtualHost>

Opciones Específicas de Directorio

Configurar diferentes opciones para diferentes directorios:

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # Directorio principal del sitio web
    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # Directorio de descargas - habilitar listado de directorios
    <Directory /var/www/example.com/public_html/downloads>
        Options +Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
        IndexOptions FancyIndexing NameWidth=* DescriptionWidth=*
    </Directory>

    # Área de administración - restringir acceso
    <Directory /var/www/example.com/public_html/admin>
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require ip 192.168.1.0/24
        Require ip 10.0.0.100

        # Autenticación adicional
        AuthType Basic
        AuthName "Área de Administración"
        AuthUserFile /etc/apache2/.htpasswd
        Require valid-user
    </Directory>

    # Recursos estáticos - tiempos largos de caché
    <Directory /var/www/example.com/public_html/assets>
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted

        <IfModule mod_expires.c>
            ExpiresActive On
            ExpiresByType image/jpg "access plus 1 year"
            ExpiresByType image/jpeg "access plus 1 year"
            ExpiresByType image/png "access plus 1 year"
            ExpiresByType image/gif "access plus 1 year"
            ExpiresByType text/css "access plus 1 month"
            ExpiresByType application/javascript "access plus 1 month"
        </IfModule>
    </Directory>
</VirtualHost>

Redirección de URL

Redirigir www a no-www (o viceversa):

# Redirigir www a no-www
<VirtualHost *:80>
    ServerName www.example.com
    Redirect permanent / http://example.com/
</VirtualHost>

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html
    # ... resto de la configuración
</VirtualHost>

# Alternativa usando mod_rewrite
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public_html

    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
        RewriteRule ^(.*)$ http://%1$1 [R=301,L]
    </IfModule>
</VirtualHost>

Páginas de Error Personalizadas

Configurar páginas de error personalizadas por virtual host:

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # Documentos de error personalizados
    ErrorDocument 400 /errors/400.html
    ErrorDocument 401 /errors/401.html
    ErrorDocument 403 /errors/403.html
    ErrorDocument 404 /errors/404.html
    ErrorDocument 500 /errors/500.html
    ErrorDocument 502 /errors/502.html
    ErrorDocument 503 /errors/503.html

    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Crear páginas de error personalizadas:

sudo mkdir -p /var/www/example.com/public_html/errors
sudo nano /var/www/example.com/public_html/errors/404.html

Ejemplo de página 404:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>404 - Página No Encontrada</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            padding: 50px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        h1 { font-size: 72px; margin: 0; }
        p { font-size: 24px; }
        a { color: #ffd700; text-decoration: none; }
    </style>
</head>
<body>
    <h1>404</h1>
    <p>¡Ups! La página que buscas no existe.</p>
    <p><a href="/">Volver a la página de inicio</a></p>
</body>
</html>

Virtual Hosts SSL/TLS

Configurar virtual hosts HTTPS:

Habilitar Módulo SSL

# Ubuntu/Debian
sudo a2enmod ssl
sudo systemctl restart apache2

# CentOS/Rocky/AlmaLinux
sudo yum install mod_ssl -y  # o dnf
sudo systemctl restart httpd

Virtual Host HTTPS Básico

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public_html

    # Motor SSL
    SSLEngine on

    # Certificados SSL
    SSLCertificateFile /etc/ssl/certs/example.com.crt
    SSLCertificateKeyFile /etc/ssl/private/example.com.key
    SSLCertificateChainFile /etc/ssl/certs/chain.crt

    # Protocolos y Cifrados SSL
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
    SSLHonorCipherOrder on

    # Cabecera HSTS
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/example-ssl-error.log
    CustomLog ${APACHE_LOG_DIR}/example-ssl-access.log combined
</VirtualHost>

# Redirección HTTP a HTTPS
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    Redirect permanent / https://example.com/
</VirtualHost>

Configuración de Let's Encrypt

Después de obtener certificados Let's Encrypt con Certbot:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public_html

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    # Configuración SSL fuerte
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    SSLHonorCipherOrder off
    SSLSessionTickets off

    # OCSP Stapling
    SSLUseStapling On
    SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"

    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Optimización de Rendimiento

Habilitar Compresión

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # Habilitar compresión
    <IfModule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
        AddOutputFilterByType DEFLATE application/javascript application/json
        AddOutputFilterByType DEFLATE application/xml application/rss+xml
        AddOutputFilterByType DEFLATE image/svg+xml

        # No comprimir imágenes
        SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|ico)$ no-gzip dont-vary

        # Manejar peculiaridades del navegador
        BrowserMatch ^Mozilla/4 gzip-only-text/html
        BrowserMatch ^Mozilla/4\.0[678] no-gzip
        BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

        # Agregar cabecera Vary
        Header append Vary User-Agent env=!dont-vary
    </IfModule>
</VirtualHost>

Caché del Navegador

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # Habilitar módulo de expiración
    <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresDefault "access plus 1 month"

        # HTML
        ExpiresByType text/html "access plus 1 hour"

        # Imágenes
        ExpiresByType image/jpg "access plus 1 year"
        ExpiresByType image/jpeg "access plus 1 year"
        ExpiresByType image/png "access plus 1 year"
        ExpiresByType image/gif "access plus 1 year"
        ExpiresByType image/svg+xml "access plus 1 year"
        ExpiresByType image/x-icon "access plus 1 year"

        # CSS y JavaScript
        ExpiresByType text/css "access plus 1 month"
        ExpiresByType application/javascript "access plus 1 month"

        # Fuentes
        ExpiresByType font/woff "access plus 1 year"
        ExpiresByType font/woff2 "access plus 1 year"
        ExpiresByType application/font-woff "access plus 1 year"
        ExpiresByType application/font-woff2 "access plus 1 year"
    </IfModule>

    # Cabeceras Cache-Control
    <IfModule mod_headers.c>
        <FilesMatch "\.(jpg|jpeg|png|gif|ico|svg)$">
            Header set Cache-Control "public, max-age=31536000, immutable"
        </FilesMatch>

        <FilesMatch "\.(css|js)$">
            Header set Cache-Control "public, max-age=2592000"
        </FilesMatch>
    </IfModule>
</VirtualHost>

Integración con PHP-FPM

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # PHP-FPM vía proxy
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
    </FilesMatch>

    # O vía mod_fastcgi
    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks +ExecCGI
        AllowOverride All
        Require all granted
    </Directory>

    # Configuración de timeout del proxy
    ProxyTimeout 300
</VirtualHost>

Configuración de Seguridad

Control de Acceso Basado en IP

<VirtualHost *:80>
    ServerName admin.example.com
    DocumentRoot /var/www/example.com/admin

    <Directory /var/www/example.com/admin>
        Options -Indexes +FollowSymLinks
        AllowOverride None

        # Permitir solo IPs específicas
        Require ip 192.168.1.0/24
        Require ip 10.0.0.100
        Require ip 203.0.113.50

        # O denegar IPs específicas
        <RequireAll>
            Require all granted
            Require not ip 192.0.2.100
        </RequireAll>
    </Directory>
</VirtualHost>

Autenticación Básica

<VirtualHost *:80>
    ServerName secure.example.com
    DocumentRoot /var/www/example.com/secure

    <Directory /var/www/example.com/secure>
        AuthType Basic
        AuthName "Área Restringida"
        AuthUserFile /etc/apache2/.htpasswd
        Require valid-user

        Options -Indexes +FollowSymLinks
        AllowOverride None
    </Directory>
</VirtualHost>

Crear archivo de contraseñas:

# Ubuntu/Debian
sudo htpasswd -c /etc/apache2/.htpasswd usuario

# CentOS/Rocky/AlmaLinux
sudo htpasswd -c /etc/httpd/.htpasswd usuario

Cabeceras de Seguridad

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # Cabeceras de seguridad
    <IfModule mod_headers.c>
        # Prevenir clickjacking
        Header always set X-Frame-Options "SAMEORIGIN"

        # Prevenir MIME sniffing
        Header always set X-Content-Type-Options "nosniff"

        # Protección XSS
        Header always set X-XSS-Protection "1; mode=block"

        # Política de Referrer
        Header always set Referrer-Policy "strict-origin-when-cross-origin"

        # Política de Seguridad de Contenido
        Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"

        # Eliminar información del servidor
        Header always unset X-Powered-By
        Header always unset Server
    </IfModule>
</VirtualHost>

Configuración de Registro de Logs

Formatos de Log Personalizados

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # Definir formato de log personalizado
    LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
    LogFormat "%h %l %u %t \"%r\" %>s %b %D" performance

    # Usar formatos personalizados
    CustomLog ${APACHE_LOG_DIR}/example-access.log vhost_combined
    CustomLog ${APACHE_LOG_DIR}/example-performance.log performance
    ErrorLog ${APACHE_LOG_DIR}/example-error.log

    # Establecer nivel de log de errores
    LogLevel warn
</VirtualHost>

Logs Separados por Subdominio

<VirtualHost *:80>
    ServerName blog.example.com
    DocumentRoot /var/www/example.com/blog

    ErrorLog /var/log/apache2/blog.example-error.log
    CustomLog /var/log/apache2/blog.example-access.log combined
</VirtualHost>

<VirtualHost *:80>
    ServerName api.example.com
    DocumentRoot /var/www/example.com/api

    ErrorLog /var/log/apache2/api.example-error.log
    CustomLog /var/log/apache2/api.example-access.log combined
</VirtualHost>

Registro Condicional

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public_html

    # No registrar archivos estáticos
    SetEnvIf Request_URI "\.(gif|jpg|jpeg|png|css|js|ico)$" dontlog

    # No registrar verificaciones de salud
    SetEnvIf Request_URI "^/health$" dontlog

    CustomLog ${APACHE_LOG_DIR}/example-access.log combined env=!dontlog
</VirtualHost>

Solución de Problemas

Probar la Configuración

Siempre probar la configuración antes de recargar:

# Ubuntu/Debian
sudo apache2ctl configtest

# CentOS/Rocky/AlmaLinux
sudo httpd -t

Verificar la Configuración del Virtual Host

# Ubuntu/Debian
sudo apache2ctl -S

# CentOS/Rocky/AlmaLinux
sudo httpd -S

La salida muestra:

  • Todos los virtual hosts configurados
  • Asignaciones de puertos
  • Valores de ServerName y ServerAlias
  • Virtual host predeterminado

Problemas Comunes

Problema: "Name or service not known"

# Asegurarse de que ServerName sea resoluble
# Agregar a /etc/hosts para pruebas:
sudo nano /etc/hosts
# Agregar: 127.0.0.1 example.com www.example.com

Problema: Se sirve el virtual host incorrecto

# Verificar el orden de virtual hosts
sudo apache2ctl -S

# Asegurarse de que ServerName coincida exactamente
# Verificar si hay un virtual host catch-all predeterminado

Problema: Errores de permiso denegado

# Verificar permisos de archivos
ls -la /var/www/example.com/

# Corregir permisos
sudo chown -R www-data:www-data /var/www/example.com  # Ubuntu/Debian
sudo chown -R apache:apache /var/www/example.com      # CentOS/Rocky

# Verificar SELinux (CentOS/Rocky/AlmaLinux)
sudo getenforce
sudo chcon -R -t httpd_sys_content_t /var/www/example.com/public_html

Mejores Prácticas

Configuración Organizada

Mantener los archivos de virtual hosts organizados:

# Convención de nombres en Ubuntu/Debian
/etc/apache2/sites-available/
├── 000-default.conf        # Predeterminado/catch-all
├── example.com.conf        # Dominio principal
├── blog.example.com.conf   # Subdominio
└── api.example.com.conf    # Subdominio de API

Documentación

Documentar cada virtual host:

#
# Configuración de Virtual Host: example.com
# Creado: 2024-01-15
# Modificado: 2024-01-20
# Propósito: Sitio web principal de la empresa
# Contactos: [email protected]
#

<VirtualHost *:80>
    ServerName example.com
    # ...
</VirtualHost>

Copias de Seguridad Regulares

Hacer copias de seguridad de las configuraciones de virtual hosts:

# Ubuntu/Debian
sudo tar -czf apache-vhosts-backup-$(date +%F).tar.gz /etc/apache2/sites-available/

# CentOS/Rocky/AlmaLinux
sudo tar -czf httpd-vhosts-backup-$(date +%F).tar.gz /etc/httpd/conf.d/

Monitoreo

Monitorear accesos y errores de virtual hosts:

# Log de acceso en tiempo real
sudo tail -f /var/log/apache2/example-access.log

# Log de errores en tiempo real
sudo tail -f /var/log/apache2/example-error.log

# Contar solicitudes por virtual host
sudo awk '{print $1}' /var/log/apache2/*-access.log | sort | uniq -c | sort -nr

Conclusión

Los virtual hosts son una característica fundamental de Apache que permite el alojamiento eficiente de múltiples sitios web en un solo servidor. Esta guía ha cubierto todo, desde virtual hosts básicos basados en nombres hasta configuraciones avanzadas de SSL/TLS, endurecimiento de seguridad y optimización de rendimiento.

Puntos clave:

  • El alojamiento virtual basado en nombres permite sitios web ilimitados en una dirección IP
  • La estructura de directorios y los permisos adecuados son esenciales para la seguridad
  • Los virtual hosts SSL/TLS requieren configuración adicional pero son críticos para la web moderna
  • La optimización de rendimiento mediante caché y compresión mejora la experiencia del usuario
  • Las cabeceras de seguridad y los controles de acceso protegen tus sitios web
  • Las pruebas y el monitoreo regulares aseguran una operación confiable

Domina la configuración de virtual hosts para administrar eficientemente múltiples sitios web, reducir costos de alojamiento y mantener entornos web seguros y de alto rendimiento. Continúa explorando temas avanzados como virtual hosts dinámicos con mod_vhost_alias, integración con herramientas de gestión de configuración y gestión automatizada de certificados SSL con Let's Encrypt.