⚙️ Nginx - Proxy Reverso
Configuración de Nginx como proxy reverso para enrutar subdominios a contenedores Docker.
Índice
- Instalación
- Conceptos Básicos
- Estructura de Archivos
- Configuración de Virtual Hosts
- Cabeceras y Proxy
- Comandos Útiles
- Buenas Prácticas
Instalación
Instalar Nginx
Verificar instalación
Resultado esperado:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded
Active: active (running)
Abrir puertos en UFW
"Nginx Full" permite: - Puerto 80 (HTTP) - Puerto 443 (HTTPS)
Probar Nginx
Abre un navegador y ve a http://YOUR_SERVER_IP
✅ Deberías ver la página: "Welcome to nginx!"
Conceptos Básicos
¿Qué es un Proxy Reverso?
Nginx actúa como intermediario entre Internet y tus aplicaciones:
Ventajas: - ✅ Un solo servidor maneja múltiples dominios/subdominios - ✅ SSL/TLS centralizado - ✅ Balanceo de carga - ✅ Caché - ✅ Compresión - ✅ Seguridad adicional
Flujo de una petición
https://labs.josejordan.dev
↓
Cloudflare
↓
Nginx (puerto 443)
↓
Comprueba server_name
↓
Redirige a localhost:3000
↓
Contenedor Docker
Estructura de Archivos
Directorios principales
/etc/nginx/
├── nginx.conf # Configuración principal (no tocar)
├── sites-available/ # Configuraciones disponibles
│ ├── default # Sitio por defecto
│ ├── labs # Tu subdominio labs
│ └── api # Tu subdominio api
├── sites-enabled/ # Configuraciones activas (symlinks)
│ ├── default -> ../sites-available/default
│ ├── labs -> ../sites-available/labs
│ └── api -> ../sites-available/api
└── snippets/ # Fragmentos reutilizables
Flujo de trabajo
- Crear configuración en
sites-available/ - Habilitar creando symlink en
sites-enabled/ - Probar configuración con
nginx -t - Recargar Nginx con
systemctl reload nginx
Configuración de Virtual Hosts
Template básico (HTTP)
server {
listen 80;
listen [::]:80;
server_name subdominio.ejemplo.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Template completo (HTTP + HTTPS)
# HTTP -> redirige a HTTPS
server {
listen 80;
listen [::]:80;
server_name labs.josejordan.dev;
return 301 https://$host$request_uri;
}
# HTTPS + proxy
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name labs.josejordan.dev;
# Certificados SSL
ssl_certificate /etc/letsencrypt/live/labs.josejordan.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/labs.josejordan.dev/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# IP real detrás de Cloudflare
real_ip_header CF-Connecting-IP;
# Forzar no-caché para apps dinámicas
proxy_hide_header Cache-Control;
proxy_hide_header Expires;
proxy_hide_header Pragma;
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
# Reverse proxy
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Cabeceras de seguridad
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# HSTS (activar cuando todo sea 100% HTTPS):
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
Crear y habilitar sitio
# Crear archivo de configuración
sudo nano /etc/nginx/sites-available/nombre-subdominio
# Habilitar el sitio (crear symlink)
sudo ln -s /etc/nginx/sites-available/nombre-subdominio /etc/nginx/sites-enabled/
# Verificar configuración
sudo nginx -t
# Recargar Nginx
sudo systemctl reload nginx
Deshabilitar sitio default (recomendado)
El sitio default puede causar conflictos:
# Deshabilitar sitio default
sudo rm /etc/nginx/sites-enabled/default
# Recargar Nginx
sudo systemctl reload nginx
Cabeceras y Proxy
Cabeceras básicas de proxy
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;
Explicación:
- Host: Dominio solicitado
- X-Real-IP: IP del cliente
- X-Forwarded-For: Cadena de IPs (proxies)
- X-Forwarded-Proto: http o https
Para WebSockets y Next.js
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
IP real detrás de Cloudflare
Esto hace que Nginx vea la IP real del visitante, no la de Cloudflare.
Cabeceras de seguridad
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-XSS-Protection "1; mode=block" always;
HSTS (HTTP Strict Transport Security)
Solo activar cuando todo funcione 100% con HTTPS:
Advertencia: Una vez activado, el navegador SOLO aceptará HTTPS durante max-age segundos (1 año en el ejemplo).
Forzar no-caché (apps dinámicas)
proxy_hide_header Cache-Control;
proxy_hide_header Expires;
proxy_hide_header Pragma;
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
Comandos Útiles
Gestión del servicio
# Ver estado
sudo systemctl status nginx
# Iniciar
sudo systemctl start nginx
# Parar
sudo systemctl stop nginx
# Reiniciar (con downtime)
sudo systemctl restart nginx
# Recargar configuración (sin downtime)
sudo systemctl reload nginx
# Habilitar al inicio
sudo systemctl enable nginx
# Deshabilitar al inicio
sudo systemctl disable nginx
Validación y testing
# Verificar sintaxis de configuración
sudo nginx -t
# Ver configuración parseada
sudo nginx -T
# Ver versión de Nginx
nginx -v
# Ver versión y módulos compilados
nginx -V
Gestión de sitios
# Ver sitios disponibles
ls -la /etc/nginx/sites-available/
# Ver sitios habilitados
ls -la /etc/nginx/sites-enabled/
# Habilitar sitio
sudo ln -s /etc/nginx/sites-available/nombre /etc/nginx/sites-enabled/
# Deshabilitar sitio
sudo rm /etc/nginx/sites-enabled/nombre
# Ver configuración de un sitio
sudo cat /etc/nginx/sites-available/nombre
Logs
# Ver logs de acceso en tiempo real
sudo tail -f /var/log/nginx/access.log
# Ver logs de errores en tiempo real
sudo tail -f /var/log/nginx/error.log
# Ver últimas 50 líneas de errores
sudo tail -n 50 /var/log/nginx/error.log
# Buscar errores específicos
sudo grep "error" /var/log/nginx/error.log
# Ver logs por sitio (si se configuran logs personalizados)
sudo tail -f /var/log/nginx/labs-access.log
Testing
# Probar desde el VPS
curl http://localhost
curl -H "Host: labs.josejordan.dev" http://localhost
# Ver headers de respuesta
curl -I https://labs.josejordan.dev
# Probar SSL
curl -v https://labs.josejordan.dev
# Test completo con tiempos
curl -w "@-" -o /dev/null -s https://labs.josejordan.dev <<'EOF'
time_namelookup: %{time_namelookup}s\n
time_connect: %{time_connect}s\n
time_appconnect: %{time_appconnect}s\n
time_redirect: %{time_redirect}s\n
time_pretransfer: %{time_pretransfer}s\n
time_starttransfer: %{time_starttransfer}s\n
----------\n
time_total: %{time_total}s\n
EOF
Buenas Prácticas
1. Siempre verificar antes de recargar
Si la configuración tiene errores, Nginx NO se recargará y mantendrá la configuración anterior.
2. Mantener sesión SSH abierta
Cuando hagas cambios importantes (SSL, redirects, etc.), mantén una sesión SSH abierta por si algo falla.
3. Usar reload en lugar de restart
# ✅ Recomendado (sin downtime)
sudo systemctl reload nginx
# ❌ Evitar (corta conexiones)
sudo systemctl restart nginx
4. Logs personalizados por sitio
server {
server_name labs.josejordan.dev;
access_log /var/log/nginx/labs-access.log;
error_log /var/log/nginx/labs-error.log;
# ... resto de configuración
}
5. Snippets para configuraciones reutilizables
Crear archivo /etc/nginx/snippets/proxy-params.conf:
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Luego incluirlo:
6. Límites de tamaño para uploads
7. Timeouts para proxies
location / {
proxy_pass http://127.0.0.1:3000;
# Timeouts (útil para APIs lentas)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
Configuración de Ejemplo Real
Archivo: /etc/nginx/sites-available/labs
# HTTP -> HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name labs.josejordan.dev;
return 301 https://$host$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name labs.josejordan.dev;
# SSL
ssl_certificate /etc/letsencrypt/live/labs.josejordan.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/labs.josejordan.dev/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Logs
access_log /var/log/nginx/labs-access.log;
error_log /var/log/nginx/labs-error.log;
# Cloudflare
real_ip_header CF-Connecting-IP;
# No cache
proxy_hide_header Cache-Control;
add_header Cache-Control "no-store, no-cache" always;
# Proxy
location / {
proxy_pass http://127.0.0.1:3000;
include snippets/proxy-params.conf;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}
Troubleshooting
Nginx no arranca
# Ver qué está usando el puerto 80
sudo netstat -tulpn | grep :80
# o
sudo ss -tulpn | grep :80
# Ver logs de error
sudo tail -f /var/log/nginx/error.log
# Verificar configuración
sudo nginx -t
Ver "Welcome to nginx!" en lugar del contenedor
El sitio default está capturando las peticiones:
Error "conflicting server name"
Dos sitios usan el mismo server_name:
Cambios no se aplican
# Verificar sintaxis primero
sudo nginx -t
# Recargar (no restart)
sudo systemctl reload nginx
# Si no funciona, reiniciar
sudo systemctl restart nginx
# Ver logs
sudo tail -f /var/log/nginx/error.log
⬅️ Anterior: Docker | Volver al índice | ➡️ Siguiente: SSL y Cloudflare