Montar un servidor web en Linux: Nginx y Apache
Guía completa para desplegar un servidor web Linux: Nginx o Apache, virtual hosts, HTTPS con Let's Encrypt, reverse proxy, seguridad y optimizaciones.
InSkillCoach
Montar un servidor web en Linux: Nginx y Apache
Alojar un sitio o una aplicación web en su propio servidor Linux es uno de los ejercicios más formativos de la administración de sistemas: implica la gestión de paquetes, los servicios systemd, la red, los certificados TLS y la seguridad. En esta guía vamos a montar un servidor web completo, desde la instalación hasta el HTTPS pasando por el reverse proxy, cubriendo los dos servidores que dominan el mercado: Nginx y Apache. Los ejemplos utilizan Ubuntu/Debian; las diferencias con la familia Red Hat se indican cuando existen.
1. Elegir Nginx o Apache
Ambos servidores son maduros, eficientes y perfectamente capaces de alojar cualquier sitio. Sin embargo, sus arquitecturas son distintas y algunos contextos favorecen a uno u otro.
| Criterio | Nginx | Apache |
|---|---|---|
| Arquitectura | Orientada a eventos, asíncrona | Procesos/hilos por conexión (MPM event moderno) |
| Contenido estático | Excelente, consumo de memoria muy bajo | Bueno |
| Conexiones simultáneas masivas | Punto fuerte histórico | Correcto con mpm_event |
| Reverse proxy / load balancing | Nativo y muy utilizado | Posible (mod_proxy) |
| Archivos .htaccess | No soportados | Soportados (configuración por directorio) |
| Ejecución de código (PHP…) | Siempre delegada (FastCGI/PHP-FPM) | Posible mediante módulos integrados (mod_php) |
| Sintaxis de configuración | Bloques concisos | Directivas de tipo XML, más verbosas |
| Caso de uso típico | Reverse proxy, estático, aplicaciones modernas | Alojamiento compartido, legacy, .htaccess necesario |
En 2026, la opción por defecto para un proyecto nuevo suele ser Nginx: reverse proxy nativo, configuración compacta y consumo de memoria estable bajo carga elevada. Apache sigue siendo imprescindible cuando sus usuarios deben modificar la configuración por directorio (.htaccess) o para aplicaciones históricas que dan por hecho su uso. El resto de la guía se centra en Nginx e indica los equivalentes de Apache en los pasos clave.
2. Instalación
Nginx
# Debian / Ubuntu
sudo apt update
sudo apt install nginx
# RHEL / Rocky / Alma
sudo dnf install nginx
sudo systemctl enable --now nginx
# Comprobar que el servicio está en marcha
systemctl status nginx
curl -I http://localhost
Apache
# Debian / Ubuntu (el paquete y el servicio se llaman apache2)
sudo apt install apache2
# RHEL / Rocky / Alma (el paquete y el servicio se llaman httpd)
sudo dnf install httpd
sudo systemctl enable --now httpd
No olvide abrir el cortafuegos:
# Ubuntu con ufw
sudo ufw allow "Nginx Full"
# RHEL con firewalld
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload
3. Estructura de la configuración
Nginx
/etc/nginx/
├── nginx.conf # configuración principal
├── conf.d/ # archivos .conf incluidos automáticamente
├── sites-available/ # sus vhosts (Debian/Ubuntu)
├── sites-enabled/ # enlaces simbólicos a los vhosts activos
└── snippets/ # fragmentos reutilizables (SSL, etc.)
En Debian/Ubuntu, cada sitio se escribe en sites-available/ y después se activa mediante un enlace simbólico en sites-enabled/. En RHEL esta convención no existe: coloque sus archivos directamente en conf.d/.
# Activar un sitio (Debian/Ubuntu)
sudo ln -s /etc/nginx/sites-available/monsite.conf /etc/nginx/sites-enabled/
# Desactivar un sitio: eliminar el enlace, nunca el archivo de origen
sudo rm /etc/nginx/sites-enabled/monsite.conf
Apache
/etc/apache2/ # Debian/Ubuntu (RHEL: /etc/httpd/)
├── apache2.conf # configuración principal
├── sites-available/ # vhosts disponibles
├── sites-enabled/ # vhosts activos
├── mods-available/ # módulos disponibles
└── mods-enabled/ # módulos activos
Apache proporciona comandos específicos para la activación:
sudo a2ensite monsite.conf # activar un vhost
sudo a2dissite monsite.conf # desactivar
sudo a2enmod rewrite ssl headers # activar módulos
4. Virtual hosts y server blocks
Un mismo servidor puede alojar varios sitios, distinguidos por su nombre de dominio. Nginx habla de «server blocks» y Apache de «virtual hosts».
Server block completo de Nginx
# /etc/nginx/sites-available/monsite.conf
server {
listen 80;
listen [::]:80;
server_name monsite.example.com;
root /var/www/monsite;
index index.html index.htm;
# Registros dedicados al sitio
access_log /var/log/nginx/monsite.access.log;
error_log /var/log/nginx/monsite.error.log;
location / {
# Servir el archivo; si no existe, el directorio; si no, 404
try_files $uri $uri/ =404;
}
# Bloquear el acceso a los archivos ocultos (.git, .env...)
location ~ /\. {
deny all;
}
}
# Crear la raíz del sitio y una página de prueba
sudo mkdir -p /var/www/monsite
echo "<h1>Bienvenue sur monsite</h1>" | sudo tee /var/www/monsite/index.html
sudo chown -R www-data:www-data /var/www/monsite
# Activar y recargar
sudo ln -s /etc/nginx/sites-available/monsite.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Virtual host equivalente de Apache
# /etc/apache2/sites-available/monsite.conf
<VirtualHost *:80>
ServerName monsite.example.com
DocumentRoot /var/www/monsite
ErrorLog ${APACHE_LOG_DIR}/monsite.error.log
CustomLog ${APACHE_LOG_DIR}/monsite.access.log combined
<Directory /var/www/monsite>
Options -Indexes
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
sudo a2ensite monsite.conf
sudo apachectl configtest && sudo systemctl reload apache2
5. HTTPS con Let’s Encrypt
Un sitio sin HTTPS ya no es aceptable: los navegadores y los motores de búsqueda penalizan el HTTP. Let’s Encrypt proporciona certificados gratuitos, y certbot automatiza todo el proceso, desde el desafío de validación hasta la renovación.
# Instalación de certbot con el plugin de Nginx
sudo apt install certbot python3-certbot-nginx
# (Apache: python3-certbot-apache; RHEL: mediante EPEL)
# Obtener el certificado Y modificar la configuración automáticamente
sudo certbot --nginx -d monsite.example.com
# Comprobar la renovación automática (timer de systemd)
systemctl list-timers | grep certbot
sudo certbot renew --dry-run
Requisitos previos: el dominio debe apuntar a la IP pública del servidor (registro DNS A/AAAA) y el puerto 80 debe ser accesible para el desafío HTTP-01. Certbot añade la redirección de HTTP a HTTPS y configura los certificados en su server block:
# Fragmento añadido por certbot en el server block
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/monsite.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/monsite.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
Los certificados de Let’s Encrypt son válidos durante 90 días; el timer de systemd instalado por el paquete los renueva automáticamente al acercarse la fecha de caducidad. Basta con ejecutar el --dry-run una vez tras la instalación.
6. Reverse proxy hacia una aplicación
Es un caso extremadamente habitual: su aplicación (Node.js, Python, Go…) escucha en un puerto local, y Nginx la publica en el puerto 443 encargándose de TLS, la compresión y los logs.
# /etc/nginx/sites-available/app.conf
# Agrupación de los backends (permite hacer load balancing más adelante)
upstream mon_app {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / {
proxy_pass http://mon_app;
# Transmitir a la aplicación la información del cliente
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Soporte de WebSockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Tiempos de espera razonables
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
}
Las cabeceras X-Forwarded-* son esenciales: sin ellas, su aplicación ve todas las peticiones como procedentes de 127.0.0.1 y cree estar en HTTP. En la familia Red Hat, piense también en el booleano de SELinux que autoriza a Nginx a abrir conexiones salientes:
sudo setsebool -P httpd_can_network_connect on
7. Cabeceras de seguridad
Algunas cabeceras HTTP protegen a sus visitantes contra el clickjacking, el sniffing MIME y las degradaciones a HTTP. Deben colocarse en el server block (o en un snippet incluido por todos sus sitios):
# /etc/nginx/snippets/security-headers.conf
# HSTS: fuerza HTTPS durante 2 años (actívelo solo cuando HTTPS sea estable)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# Prohíbe la inclusión del sitio en un iframe de terceros (clickjacking)
add_header X-Frame-Options "SAMEORIGIN" always;
# Prohíbe al navegador adivinar los tipos MIME
add_header X-Content-Type-Options "nosniff" always;
# Limita la información de procedencia transmitida a sitios de terceros
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# En cada server block HTTPS
include snippets/security-headers.conf;
Añada una Content-Security-Policy adaptada a su sitio cuando conozca con precisión sus fuentes de scripts y estilos: es la cabecera más eficaz, pero un ajuste demasiado estricto rompe el sitio. Pruebe su configuración en securityheaders.com y la parte TLS en ssllabs.com.
Oculte también la versión del servidor, una información inútil que no conviene ofrecer a los escáneres:
# En http {} de /etc/nginx/nginx.conf
server_tokens off;
8. Logs y rotación
Leer los registros
# Seguir el tráfico en tiempo real
sudo tail -f /var/log/nginx/access.log
# Seguir los errores
sudo tail -f /var/log/nginx/error.log
# Top 10 de las IP más activas
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# Contar los códigos de respuesta
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
Rotación con logrotate
Los paquetes de Nginx y Apache ya instalan una configuración de logrotate; compruébela y adapte la retención a sus necesidades:
# /etc/logrotate.d/nginx (proporcionado por el paquete, fragmento)
/var/log/nginx/*.log {
daily # rotación diaria
rotate 14 # conservar 14 archivos
compress # comprimir los archivos
delaycompress # excepto el más reciente
notifempty
sharedscripts
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
# Probar sin esperar a la noche
sudo logrotate -d /etc/logrotate.d/nginx
Si crea registros por sitio como en nuestros ejemplos, el patrón *.log los cubre automáticamente.
9. Probar y recargar sin cortes
Un reflejo que conviene interiorizar definitivamente: no reinicie nunca un servidor web sin haber validado la configuración, y prefiera siempre la recarga al reinicio.
# 1. Validar la sintaxis y la coherencia de la configuración
sudo nginx -t
# Apache: sudo apachectl configtest
# 2. Recargar: los workers terminan sus peticiones en curso,
# y se inician nuevos workers con la nueva configuración.
# No se corta ninguna conexión.
sudo systemctl reload nginx
# En una sola línea, la recarga condicionada a la prueba
sudo nginx -t && sudo systemctl reload nginx
systemctl restart corta bruscamente las conexiones en curso; resérvelo para los casos en que la recarga no sea suficiente (cambio de puertos de escucha, actualización del binario).
10. Optimizaciones básicas
Compresión gzip
# En http {} de /etc/nginx/nginx.conf
gzip on;
gzip_comp_level 5; # buen compromiso CPU / tamaño
gzip_min_length 256; # inútil por debajo de 256 bytes
gzip_types text/plain text/css application/json application/javascript
application/xml image/svg+xml;
# Las imágenes JPEG/PNG/WebP ya están comprimidas: no las incluya
Caché de archivos estáticos
Pida a los navegadores que conserven durante mucho tiempo los recursos que no cambian:
# En el server block
location ~* \.(css|js|jpg|jpeg|png|webp|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off; # también aligera los registros
}
Esta estrategia supone que sus archivos cambian de nombre en cada versión (hash en el nombre, como hacen todos los bundlers modernos). De lo contrario, reduzca la duración.
Procesos workers
# En /etc/nginx/nginx.conf
worker_processes auto; # un worker por núcleo de CPU: el ajuste correcto
events {
worker_connections 1024; # conexiones simultáneas por worker
}
auto es el valor por defecto de los paquetes recientes y sirve para la práctica totalidad de los casos. Aumente worker_connections solo si sus registros muestran que se alcanza el límite, y piense entonces en el límite de descriptores de archivo (worker_rlimit_nofile).
Buenas prácticas
- Un archivo de configuración por sitio, activado mediante enlace simbólico; no toque los archivos proporcionados por el paquete.
- Valide con
nginx -toapachectl configtestantes de cada recarga, idealmente en la misma línea de comandos. - Prefiera
reloadarestart: cero cortes para sus visitantes. - HTTPS en todas partes, redirección de HTTP a HTTPS y un
certbot renew --dry-runtras la instalación para validar la renovación automática. - Añada las cabeceras de seguridad en un snippet compartido y pruebe el resultado en securityheaders.com.
- Registros dedicados por sitio, rotación verificada y un ojo atento a
error.log. - Detrás de un reverse proxy, transmita siempre
Host,X-Forwarded-ForyX-Forwarded-Protoa la aplicación. - Haga copia de seguridad de
/etc/nginx(o/etc/apache2) y de/etc/letsencrypt: la configuración de un servidor web se reconstruye en minutos cuando está versionada.
Conclusión
Ya dispone de un servidor web completo: varios sitios en virtual hosts, HTTPS automático con Let’s Encrypt, reverse proxy hacia sus aplicaciones, cabeceras de seguridad, registros controlados y optimizaciones básicas. Nginx y Apache son programas de una fiabilidad notable: bien configurados, funcionan durante años sin intervención. Los siguientes pasos de su progresión: el load balancing entre varios backends, el almacenamiento en caché con proxy_cache, la limitación de tasa (limit_req) contra los abusos y la automatización de toda esta configuración con Ansible o Terraform para reconstruir su servidor con un solo comando.
Acerca de InSkillCoach
Experto en formación y tecnologías
Coach especializado en tecnologías avanzadas e IA, impulsado por GNeurone Inc.
Certificaciones:
- AWS Certified Solutions Architect – Professional
- Certificaciones de Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comentarios
Los comentarios funcionan con GitHub Discussions
Inicia sesión con GitHub para participar en la discusión