Los virtual hosts permiten que un único servidor Apache sirva varios sitios web, cada uno con su dominio, su directorio y sus logs. Apache elige qué sitio responder comparando la cabecera Host de la petición con las directivas ServerName y ServerAlias de cada virtual host. En este tutorial configurarás dos dominios en Ubuntu 24.04, activarás PHP-FPM en uno de ellos, protegerás ambos con HTTPS y redirigirás www al dominio principal.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS y Apache instalado, con el perfil
Apache Fullpermitido en UFW. Si aún no lo tienes, sigue primero la guía de instalación de Apache en Ubuntu 24.04. - Un usuario no root con privilegios
sudo. - Dos dominios con registros DNS
Aque apunten a la IP pública del servidor, tanto para el dominio raíz como parawww. En esta guía se usansite1.your_domainysite2.your_domaincomo ejemplo: sustitúyelos por los tuyos.
Cómo elige Apache el virtual host
Casi todos los sitios usan virtual hosts basados en nombre: todos comparten la misma IP y el puerto 80 o 443, y Apache los distingue por el nombre de dominio. Para cada petición, Apache:
- Busca los virtual hosts que coinciden con la IP y el puerto de la conexión (
*:80). - Entre ellos, elige el que tenga un
ServerNameoServerAliasigual a la cabeceraHost. - Si ninguno coincide, responde con el primero que cargó para ese puerto, que actúa como sitio por defecto.
El punto 3 explica el problema más habitual: si un nombre no está bien escrito, Apache no da error, simplemente muestra otro sitio.
Paso 1: Crear los directorios de los sitios
Crea un directorio por sitio dentro de /var/www. Usar un subdirectorio public_html deja espacio fuera de la raíz web para archivos que no deben publicarse (configuración, copias, logs de la aplicación):
sudo mkdir -p /var/www/site1.your_domain/public_html
sudo mkdir -p /var/www/site2.your_domain/public_html
Asigna los directorios a tu usuario para desplegar sin sudo. Apache (www-data) solo necesita leer, y los permisos por defecto (755 en directorios, 644 en archivos) ya lo permiten:
sudo chown -R $USER:$USER /var/www/site1.your_domain /var/www/site2.your_domain
Notasi la aplicación necesita escribir en algún directorio (subidas, caché), da permisos de escritura a
www-datasolo en ese directorio, por ejemplo consudo chown -R www-data:www-data /var/www/site1.your_domain/public_html/uploads. Nunca useschmod 777.
Paso 2: Crear una página de prueba en cada sitio
Crea un index.html distinto en cada sitio para distinguir cuál responde:
echo '<h1>Sitio 1 funciona</h1>' > /var/www/site1.your_domain/public_html/index.html
echo '<h1>Sitio 2 funciona</h1>' > /var/www/site2.your_domain/public_html/index.html
Paso 3: Crear el primer virtual host
En Ubuntu cada virtual host es un archivo en /etc/apache2/sites-available/. Nómbralo con el dominio y la extensión .conf, que a2ensite exige:
sudo nano /etc/apache2/sites-available/site1.your_domain.conf
<VirtualHost *:80>
ServerName site1.your_domain
ServerAlias www.site1.your_domain
ServerAdmin [email protected]_domain
DocumentRoot /var/www/site1.your_domain/public_html
<Directory /var/www/site1.your_domain/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/site1.your_domain_error.log
CustomLog ${APACHE_LOG_DIR}/site1.your_domain_access.log combined
</VirtualHost>
Las directivas clave:
ServerNamees el nombre principal yServerAliasacepta nombres adicionales (puedes poner varios separados por espacios, o comodines como*.site1.your_domain).DocumentRootes la carpeta que se publica.Options -Indexesimpide listar directorios sin archivo índice.AllowOverride Allpermite.htaccess. Si tu aplicación no lo usa, ponNonepara que Apache no lo busque en cada petición.ErrorLogyCustomLogseparan los logs por sitio, lo que facilita mucho el diagnóstico.
Paso 4: Crear el segundo virtual host
Copia el archivo y sustituye el nombre del dominio:
sudo cp /etc/apache2/sites-available/site1.your_domain.conf /etc/apache2/sites-available/site2.your_domain.conf
sudo sed -i 's/site1\.your_domain/site2.your_domain/g' /etc/apache2/sites-available/site2.your_domain.conf
Revisa el resultado:
grep -E 'ServerName|ServerAlias|DocumentRoot' /etc/apache2/sites-available/site2.your_domain.conf
ServerName site2.your_domain
ServerAlias www.site2.your_domain
DocumentRoot /var/www/site2.your_domain/public_html
Paso 5: Activar los sitios
Activa los dos virtual hosts. a2ensite crea los enlaces en sites-enabled/:
sudo a2ensite site1.your_domain.conf site2.your_domain.conf
Desactiva el sitio por defecto de Ubuntu para que no responda a nombres desconocidos con la página de bienvenida:
sudo a2dissite 000-default.conf
Comprueba la sintaxis y recarga Apache:
sudo apache2ctl configtest
sudo systemctl reload apache2
Syntax OK
Paso 6: Verificar qué virtual hosts ha cargado Apache
apache2ctl -S muestra cómo ha interpretado Apache la configuración: qué virtual hosts hay en cada puerto, sus alias y cuál es el predeterminado:
sudo apache2ctl -S
VirtualHost configuration:
*:80 is a NameVirtualHost
default server site1.your_domain (/etc/apache2/sites-enabled/site1.your_domain.conf:1)
port 80 namevhost site1.your_domain (/etc/apache2/sites-enabled/site1.your_domain.conf:1)
alias www.site1.your_domain
port 80 namevhost site2.your_domain (/etc/apache2/sites-enabled/site2.your_domain.conf:1)
alias www.site2.your_domain
Ahora prueba cada dominio. Con --resolve no necesitas esperar al DNS ni editar /etc/hosts:
curl --resolve site1.your_domain:80:127.0.0.1 http://site1.your_domain
curl --resolve site2.your_domain:80:127.0.0.1 http://site2.your_domain
<h1>Sitio 1 funciona</h1>
<h1>Sitio 2 funciona</h1>
Si el DNS ya apunta al servidor, abre ambos dominios en el navegador.
Paso 7: Ejecutar PHP en un sitio con PHP-FPM
Con PHP-FPM, Apache mantiene el MPM event y PHP corre en su propio servicio. Instala PHP-FPM y activa los módulos de proxy FastCGI:
sudo apt install php8.3-fpm
sudo a2enmod proxy_fcgi setenvif
Podrías activar PHP para todos los sitios con sudo a2enconf php8.3-fpm. Para limitarlo al sitio 2, añade este bloque dentro de su <VirtualHost *:80>:
sudo nano /etc/apache2/sites-available/site2.your_domain.conf
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
DirectoryIndex index.php index.html
Reinicia Apache para cargar los módulos nuevos y crea un archivo de prueba:
sudo apache2ctl configtest
sudo systemctl restart apache2
echo '<?php echo "PHP " . PHP_VERSION . " en site2\n";' > /var/www/site2.your_domain/public_html/info.php
curl --resolve site2.your_domain:80:127.0.0.1 http://site2.your_domain/info.php
PHP 8.3.6 en site2
Borra el archivo de prueba al terminar:
rm /var/www/site2.your_domain/public_html/info.php
Consejosi alojas sitios de clientes distintos, crea un pool de PHP-FPM por sitio en
/etc/php/8.3/fpm/pool.d/, con su propio usuario y socket, y apunta cadaSetHandlera su socket. Así un sitio comprometido no puede leer los archivos de otro.
Paso 8: Activar HTTPS en todos los sitios
Instala Certbot con el plugin de Apache:
sudo apt install certbot python3-certbot-apache
Solicita un certificado por sitio. Certbot crea un virtual host *:443 para cada uno (archivos *-le-ssl.conf) y añade la redirección de HTTP a HTTPS:
sudo certbot --apache -d site1.your_domain -d www.site1.your_domain
sudo certbot --apache -d site2.your_domain -d www.site2.your_domain
Comprueba que ahora hay virtual hosts en el puerto 443:
sudo apache2ctl -S | grep 443
*:443 is a NameVirtualHost
port 443 namevhost site1.your_domain (/etc/apache2/sites-enabled/site1.your_domain-le-ssl.conf:2)
port 443 namevhost site2.your_domain (/etc/apache2/sites-enabled/site2.your_domain-le-ssl.conf:2)
Confirma que la renovación automática funcionará:
sudo certbot renew --dry-run
Paso 9: Redirigir www al dominio principal
Ahora cada sitio responde igual en site1.your_domain y en www.site1.your_domain, lo que duplica las URL a efectos de SEO. Unifícalas con una redirección 301 en el virtual host HTTPS que ha creado Certbot. Asegúrate de que mod_rewrite está activo (Certbot suele activarlo al crear la redirección a HTTPS):
sudo a2enmod rewrite
sudo nano /etc/apache2/sites-available/site1.your_domain-le-ssl.conf
Añade estas líneas dentro del bloque <VirtualHost *:443>, por ejemplo justo después de DocumentRoot:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.site1\.your_domain$ [NC]
RewriteRule ^ https://site1.your_domain%{REQUEST_URI} [R=301,L]
La regla conserva la ruta, así que /contacto en www redirige a /contacto en el dominio principal. Comprueba y recarga:
sudo apache2ctl configtest
sudo systemctl restart apache2
curl -I https://www.site1.your_domain/contacto
HTTP/1.1 301 Moved Permanently
Location: https://site1.your_domain/contacto
Repite el cambio en site2.your_domain-le-ssl.conf con su dominio. Las peticiones a http://www... pasan primero por la redirección a HTTPS de Certbot y después por esta.
Paso 10: Añadir o retirar un sitio
Con esta estructura, gestionar sitios es repetitivo y seguro:
- Para añadir uno: crea su directorio, copia un
.confexistente, cambia el dominio, ejecutasudo a2ensite nuevo.conf,sudo apache2ctl configtestysudo systemctl reload apache2, y después Certbot. - Para retirarlo temporalmente sin borrar nada:
sudo a2dissite site2.your_domain.confysudo a2dissite site2.your_domain-le-ssl.conf, y recarga Apache.
Solución de problemas
Todos los dominios muestran el mismo sitio: el nombre de la petición no coincide con ningún ServerName o ServerAlias y Apache responde con el virtual host por defecto. Revisa la salida de sudo apache2ctl -S y comprueba que el dominio aparece exactamente igual.
ERROR: Site site1.your_domain does not exist! al ejecutar a2ensite: el archivo no está en sites-available/ o no termina en .conf.
403 Forbidden: falta Require all granted en el bloque <Directory>, la ruta de <Directory> no coincide con DocumentRoot, o www-data no puede leer la ruta. namei -l /var/www/site1.your_domain/public_html/index.html muestra los permisos de cada nivel.
El navegador descarga el archivo PHP en lugar de ejecutarlo: el bloque FilesMatch no está en ese virtual host (ni en su versión -le-ssl.conf, si lo añadiste después de ejecutar Certbot) o php8.3-fpm no está activo. Comprueba el servicio con systemctl status php8.3-fpm.
503 Service Unavailable en archivos PHP: Apache no encuentra el socket de PHP-FPM. Verifica que existe /run/php/php8.3-fpm.sock y que la ruta del SetHandler es exactamente esa.
Conclusión
Has configurado dos virtual hosts en Apache sobre Ubuntu 24.04, cada uno con su directorio y sus logs, PHP-FPM en uno de ellos, HTTPS con renovación automática y una redirección de www al dominio principal. Como siguientes pasos puedes:
- Crear un pool de PHP-FPM por sitio para aislar aplicaciones de distintos clientes.
- Mover las reglas de
.htaccessal virtual host y usarAllowOverride Nonepara ganar rendimiento. - Programar copias de seguridad de
/var/wwwy/etc/apache2.
