Skip to content

Apache ​

Apache HTTP Server es el servidor web más veterano y sigue presente en la mayoría de hostings compartidos. Su gran ventaja es el archivo .htaccess, que permite configurar cada carpeta sin tocar la configuración global. En esta lección lo instalarás en tu VPS, crearás virtual hosts para varios dominios y dominarás mod_rewrite.

Concepto ​

  • Virtual host: un bloque de configuración por sitio. Con uno solo, Apache puede servir clientea.com y clienteb.com desde el mismo servidor, decidiendo por el nombre que pide el navegador.
  • Módulos: funciones que se activan o desactivan (rewrite, headers, ssl, expires, deflate…).
  • .htaccess: archivo de configuración por carpeta, leído en cada petición. Cómodo, pero algo más lento que poner las reglas en el virtual host.

Instalación (Ubuntu 24.04) ​

bash
sudo apt update
sudo apt install apache2 -y

# Permitir HTTP y HTTPS en el firewall
sudo ufw allow 'Apache Full'

# Comprobar
sudo systemctl status apache2
curl -I http://localhost

Si visitas la IP del servidor verás la página "Apache2 Ubuntu Default Page".

Estructura de archivos ​

txt
/etc/apache2/
├── apache2.conf          configuración global
├── ports.conf            puertos que escucha (80, 443)
├── sites-available/      virtual hosts disponibles
│   └── 000-default.conf
├── sites-enabled/        enlaces a los sitios activos
├── mods-available/       módulos disponibles
├── mods-enabled/         módulos activos
└── conf-available/       configuraciones extra
/var/www/html/            sitio por defecto
/var/log/apache2/         access.log y error.log

Comandos propios de Apache en Ubuntu ​

bash
sudo a2ensite misitio.conf     # activar sitio
sudo a2dissite 000-default.conf # desactivar sitio
sudo a2enmod rewrite           # activar módulo
sudo a2dismod autoindex        # desactivar módulo
sudo apachectl configtest      # revisar sintaxis (debe decir "Syntax OK")
sudo systemctl reload apache2  # aplicar cambios sin cortar conexiones

Ejemplo: virtual hosts para dos clientes ​

1. Carpetas ​

bash
sudo mkdir -p /var/www/clientea.com/public
sudo mkdir -p /var/www/clienteb.com/public
sudo chown -R $USER:www-data /var/www/clientea.com /var/www/clienteb.com

echo "<h1>Cliente A funciona</h1>" > /var/www/clientea.com/public/index.html
echo "<h1>Cliente B funciona</h1>" > /var/www/clienteb.com/public/index.html

2. Virtual host ​

bash
sudo nano /etc/apache2/sites-available/clientea.com.conf
apache
# /etc/apache2/sites-available/clientea.com.conf
<VirtualHost *:80>
    ServerName clientea.com
    ServerAlias www.clientea.com
    ServerAdmin admin@clientea.com

    DocumentRoot /var/www/clientea.com/public

    <Directory /var/www/clientea.com/public>
        Options -Indexes +FollowSymLinks
        # Permite que .htaccess sobrescriba la configuración
        AllowOverride All
        Require all granted
    </Directory>

    # Logs separados por sitio
    ErrorLog ${APACHE_LOG_DIR}/clientea.com-error.log
    CustomLog ${APACHE_LOG_DIR}/clientea.com-access.log combined
</VirtualHost>

Repite para clienteb.com cambiando nombres y rutas.

3. Activar ​

bash
sudo a2ensite clientea.com.conf clienteb.com.conf
sudo a2dissite 000-default.conf
sudo a2enmod rewrite headers expires deflate
sudo apachectl configtest
sudo systemctl reload apache2

Probar sin DNS

Antes de cambiar el DNS puedes probar desde tu computadora editando el archivo hosts (Linux/Mac: /etc/hosts; Windows: C:\Windows\System32\drivers\etc\hosts, como administrador) y agregando 203.0.113.10 clientea.com. O con curl: curl -H "Host: clientea.com" http://203.0.113.10.

.htaccess y mod_rewrite ​

mod_rewrite reescribe o redirige URLs según reglas. La estructura es:

apache
RewriteEngine On
RewriteCond  <qué revisar>  <patrón>   [banderas]
RewriteRule  <patrón de URL>  <destino>  [banderas]

Banderas más usadas: L (última regla), R=301 (redirección permanente), NC (sin distinguir mayúsculas), QSA (conservar la query string).

Recetas listas para copiar ​

Forzar HTTPS:

apache
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [L,R=301]

Quitar www:

apache
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [L,R=301]

URLs amigables a un front controller (PHP, Laravel, Slim): /productos/15 se procesa en index.php.

apache
RewriteEngine On
# Si no es un archivo ni carpeta real…
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# …mándalo a index.php conservando los parámetros
RewriteRule ^ index.php [QSA,L]

En PHP lees la ruta con $_SERVER['REQUEST_URI'].

URL bonita a parámetro: /blog/mi-articulo → articulo.php?slug=mi-articulo

apache
RewriteEngine On
RewriteRule ^blog/([a-z0-9-]+)/?$ articulo.php?slug=$1 [L,QSA]

Fallback para SPA (React/Vue):

apache
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

Redirigir una página vieja:

apache
Redirect 301 /contacto.html /contacto
Redirect 301 /servicios-viejos https://clientea.com/servicios

Seguridad, caché y compresión ​

Este .htaccess (o su equivalente dentro del virtual host) es un buen punto de partida para producción:

apache
# ---------- Seguridad ----------
Options -Indexes
ServerSignature Off

<FilesMatch "^\.|\.(env|sql|log|ini|bak|sh)$">
    Require all denied
</FilesMatch>

<IfModule mod_headers.c>
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>

# ---------- Caché del navegador ----------
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresDefault "access plus 1 hour"
    ExpiresByType text/html "access plus 0 seconds"
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

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

# ---------- Páginas de error ----------
ErrorDocument 404 /404.html
ErrorDocument 500 /500.html

Para ocultar la versión de Apache en todo el servidor, edita /etc/apache2/conf-available/security.conf:

apache
ServerTokens Prod
ServerSignature Off

Logs ​

bash
# Errores en tiempo real
sudo tail -f /var/log/apache2/clientea.com-error.log

# Peticiones que devolvieron 404
sudo awk '$9 == 404 {print $7}' /var/log/apache2/clientea.com-access.log | sort | uniq -c | sort -rn | head

Error común: el .htaccess "no hace nada"

Casi siempre es porque el virtual host tiene AllowOverride None (el valor por defecto) o porque mod_rewrite no está activo. Revisa: AllowOverride All en el <Directory> y sudo a2enmod rewrite && sudo systemctl reload apache2.

Error común: 403 Forbidden

Apache no puede leer la carpeta. Revisa permisos (carpetas 755, archivos 644) y que exista Require all granted en el bloque <Directory>.

Ejercicios ​

  1. Crea un virtual host para portafolio.test con DocumentRoot /var/www/portafolio/public y logs propios.
  2. Escribe una regla que convierta /producto/25 en producto.php?id=25 aceptando solo números.
  3. ¿Qué comando usas antes de recargar Apache para evitar tumbar todos los sitios por un error de sintaxis?
Ver solución
apache
<VirtualHost *:80>
    ServerName portafolio.test
    DocumentRoot /var/www/portafolio/public
    <Directory /var/www/portafolio/public>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/portafolio-error.log
    CustomLog ${APACHE_LOG_DIR}/portafolio-access.log combined
</VirtualHost>
apache
RewriteEngine On
RewriteRule ^producto/([0-9]+)/?$ producto.php?id=$1 [L,QSA]
  1. sudo apachectl configtest.

Resumen ​

  • Instala con apt install apache2; configuraciones en /etc/apache2.
  • Un virtual host por dominio en sites-available, activado con a2ensite.
  • AllowOverride All habilita .htaccess; a2enmod rewrite habilita mod_rewrite.
  • Recetas clave: forzar HTTPS, quitar www, front controller y fallback SPA.
  • Siempre apachectl configtest antes de reload.
  • Siguiente: Nginx.

Academia Web — aprende, copia y construye.