Objetivo del proyecto final
Vas a desplegar una aplicación web completa donde Nginx cumple dos roles en un mismo sitio: (1) servir el frontend estático (HTML/CSS/JS) y (2) actuar como reverse proxy hacia una API. Opcionalmente, la API podrá estar replicada en varios nodos para balanceo. El resultado será una configuración mantenible (archivos por sitio), con endpoints claros, políticas básicas de caché/seguridad y un flujo de verificación y rollback seguro.
Arquitectura y endpoints
Componentes
- Nginx: punto de entrada HTTP/HTTPS, terminación TLS, caché de estáticos, proxy a la API.
- Frontend: archivos estáticos en disco (por ejemplo, build de React/Vue/Angular o HTML simple).
- API: servicio(s) backend escuchando en localhost o en red privada (por ejemplo, 127.0.0.1:3000 o 10.0.0.10:3000).
Endpoints principales recomendados
/: frontend (SPA o sitio estático)./assets/o/_static/: recursos versionados (caché agresiva)./api/: proxy hacia backend(s) API./health: salud del edge (Nginx) o passthrough a la API (según tu diseño).
Estructura limpia de configuración (archivos por sitio)
La idea es que el sitio tenga su propio archivo y que los fragmentos reutilizables (cabeceras comunes, parámetros de proxy, etc.) vivan en snippets/. Un esquema típico:
/etc/nginx/ ├─ nginx.conf ├─ conf.d/ ├─ sites-available/ │ └─ app.example.com.conf ├─ sites-enabled/ │ └─ app.example.com.conf -> ../sites-available/app.example.com.conf └─ snippets/ ├─ proxy_api.conf ├─ security_headers.conf └─ ssl_params.confSi tu distribución no usa sites-available/sites-enabled, puedes adaptar el patrón con conf.d/, pero mantén el principio: un archivo por sitio y snippets para piezas repetidas.
Preparación del proyecto (rutas y permisos)
1) Directorios de despliegue
Ejemplo de rutas (ajústalas a tu entorno):
- Frontend:
/var/www/app/current - Releases (para rollback):
/var/www/app/releases/2026-02-03_1200 - Logs del sitio (opcional):
/var/log/nginx/app/
Un enfoque práctico es desplegar el frontend en un directorio de release y luego apuntar current a ese release mediante un symlink. Así puedes revertir rápido sin tocar Nginx.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
2) Variables de entorno (mentalmente) y puertos
- API en un nodo:
127.0.0.1:3000 - API con balanceo:
10.0.0.10:3000,10.0.0.11:3000,10.0.0.12:3000
Configuración del sitio: estáticos + API (con opción de balanceo)
1) Snippet de proxy para la API
Crea /etc/nginx/snippets/proxy_api.conf con parámetros consistentes para el proxy:
proxy_http_version 1.1;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_connect_timeout 5s;proxy_send_timeout 30s;proxy_read_timeout 30s;proxy_buffering on;proxy_buffers 16 16k;proxy_buffer_size 16k;Esto estandariza cabeceras y timeouts. Si tu API usa WebSockets, añade Upgrade/Connection en un snippet específico para ese endpoint.
2) Snippet de cabeceras básicas
Crea /etc/nginx/snippets/security_headers.conf:
add_header X-Content-Type-Options nosniff always;add_header X-Frame-Options DENY always;add_header Referrer-Policy strict-origin-when-cross-origin always;Para una SPA moderna, Content-Security-Policy requiere ajuste fino; si no lo tienes probado, es preferible no bloquear por defecto en este proyecto final.
3) (Opcional) Upstream para balanceo de la API
En el archivo del sitio o en un archivo incluido, define un upstream. Ejemplo:
upstream api_upstream { least_conn; server 10.0.0.10:3000 max_fails=3 fail_timeout=10s; server 10.0.0.11:3000 max_fails=3 fail_timeout=10s; server 10.0.0.12:3000 max_fails=3 fail_timeout=10s; keepalive 32;}Si solo tienes una API local, omite el upstream y usa proxy_pass http://127.0.0.1:3000;.
4) Archivo del sitio (server block)
Crea /etc/nginx/sites-available/app.example.com.conf. Ejemplo completo (ajusta dominio, rutas y certificados):
server { listen 80; server_name app.example.com; return 301 https://$host$request_uri;}server { listen 443 ssl http2; 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; include /etc/nginx/snippets/ssl_params.conf; include /etc/nginx/snippets/security_headers.conf; access_log /var/log/nginx/app.access.log; error_log /var/log/nginx/app.error.log warn; root /var/www/app/current; index index.html; location = /health { default_type text/plain; return 200 'ok'; } location ^~ /assets/ { try_files $uri =404; expires 30d; add_header Cache-Control "public, max-age=2592000, immutable"; } location / { try_files $uri $uri/ /index.html; expires 5m; add_header Cache-Control "public, max-age=300"; } location ^~ /api/ { include /etc/nginx/snippets/proxy_api.conf; proxy_pass http://api_upstream; proxy_intercept_errors on; } location = /api { return 301 /api/; } client_max_body_size 10m;}Notas prácticas:
- SPA routing:
try_files ... /index.htmlpermite rutas del frontend sin 404. - Caché: recursos versionados en
/assets/conimmutable; HTML con caché corta para facilitar despliegues. - API: se agrupa bajo
/api/para separar claramente frontend y backend.
5) Activar el sitio
En distribuciones con sites-enabled:
ln -s /etc/nginx/sites-available/app.example.com.conf /etc/nginx/sites-enabled/app.example.com.confSi ya existe, revisa que el symlink apunte a la versión correcta.
Políticas básicas de caché y seguridad aplicadas al proyecto
Caché: reglas mínimas que funcionan bien
- Assets versionados (hash en nombre): caché largo +
immutable. - HTML: caché corto para que el navegador reciba rápido la nueva versión tras despliegue.
- API: por defecto sin caché en Nginx (a menos que tengas endpoints idempotentes y estrategia clara). Si quieres forzarlo, hazlo por endpoint y con métricas.
Seguridad: mínimos razonables para el proyecto
- Redirección HTTP→HTTPS.
- Cabeceras anti-sniff y anti-iframe.
- Límites de tamaño de carga (
client_max_body_size) acordes a tu API. - Separación de rutas:
/api/no comparte reglas de caché del frontend.
Pasos de verificación (checklist operativo)
1) Validación de configuración antes de recargar
nginx -tDebe indicar sintaxis correcta y que puede cargar los archivos incluidos.
2) Recarga sin downtime
nginx -s reloadLa recarga aplica cambios sin cortar conexiones activas en la mayoría de escenarios.
3) Pruebas con curl: frontend
Comprueba redirección a HTTPS:
curl -I http://app.example.com/Esperado: 301 y cabecera Location: https://....
Comprueba HTML principal:
curl -I https://app.example.com/Esperado: 200, Content-Type: text/html y Cache-Control con max-age corto.
Comprueba assets con caché largo:
curl -I https://app.example.com/assets/app.abcdef1234.jsEsperado: 200 y Cache-Control: public, ... immutable.
4) Pruebas con curl: API
Endpoint de salud (si existe en tu API):
curl -i https://app.example.com/api/healthEsperado: 200 (o el código que defina tu API) y respuesta consistente.
Verifica que Nginx reenvía cabeceras útiles:
curl -i https://app.example.com/api/whoamiSi tu API expone diagnóstico, valida que reciba X-Forwarded-For y X-Forwarded-Proto.
5) Revisión de logs (confirmar rutas y errores)
Mientras haces pruebas, inspecciona:
tail -f /var/log/nginx/app.access.logtail -f /var/log/nginx/app.error.logQué buscar:
- 404 en assets: suele indicar ruta de
rootincorrecta o build incompleto. - 502/504 en
/api/: backend caído, DNS/red, o timeouts insuficientes. - 301 inesperados: conflictos de
locationo normalizaciones (por ejemplo/apivs/api/).
6) Validación de TLS
Verifica el handshake y el certificado presentado:
openssl s_client -connect app.example.com:443 -servername app.example.com < /dev/nullComprueba:
- El CN/SAN corresponde al dominio.
- La cadena es válida (sin errores de verificación).
- El servidor responde con el certificado esperado (no uno por defecto).
Procedimiento de rollback (configuración y despliegue)
1) Versionar y respaldar el archivo del sitio antes de cambios
Antes de editar, guarda una copia con marca de tiempo:
cp /etc/nginx/sites-available/app.example.com.conf /etc/nginx/sites-available/app.example.com.conf.bak_$(date +%Y%m%d_%H%M%S)2) Rollback de configuración (si algo falla)
Si tras un cambio el sitio falla o nginx -t no pasa:
- Restaura el backup:
cp /etc/nginx/sites-available/app.example.com.conf.bak_YYYYMMDD_HHMMSS /etc/nginx/sites-available/app.example.com.conf- Valida y recarga:
nginx -t && nginx -s reload3) Rollback del frontend (si usas releases)
Si el problema es del build estático (no de Nginx), revierte el symlink:
ln -sfn /var/www/app/releases/2026-02-02_1800 /var/www/app/currentLuego fuerza que Nginx sirva el contenido actualizado (normalmente no requiere recarga si solo cambias archivos, pero puedes recargar si cambiaste permisos o rutas):
nginx -s reloadCriterios de éxito medibles (check final)
| Criterio | Cómo medir | Resultado esperado |
|---|---|---|
| Disponibilidad del sitio | curl -I https://app.example.com/ | 200 consistente en múltiples intentos |
| Redirección a HTTPS | curl -I http://app.example.com/ | 301 a https://... |
| Frontend sirve SPA correctamente | curl -I https://app.example.com/ruta/inexistente | 200 (sirve index.html) |
| Caché de assets | curl -I https://app.example.com/assets/... | Cache-Control con max-age alto e immutable |
| API accesible detrás de Nginx | curl -i https://app.example.com/api/health | 200 (o código definido) y latencia estable |
| Estabilidad tras recargas | Ejecutar nginx -t y nginx -s reload varias veces mientras haces curl | Sin caídas, sin picos de 5xx en logs |
| TLS válido | openssl s_client ... | Certificado correcto, cadena válida, SNI correcto |
| Observación de errores | tail de logs durante pruebas | Sin 502/504 persistentes, sin 404 inesperados en assets |