Logs: tu “caja negra” para diagnosticar Nginx
En Nginx, la observabilidad básica empieza por dos archivos: access.log (qué solicitudes llegaron y cómo se respondieron) y error.log (por qué algo falló o qué decisiones internas tomó Nginx). Si aprendes a leerlos y a ajustar su formato/nivel, podrás resolver la mayoría de incidentes sin adivinar.
access.log: qué registra y cómo interpretarlo
El access.log registra una línea por solicitud. Normalmente incluye IP, método, URL, código de estado, tamaño de respuesta, referer y user-agent. Lo importante para troubleshooting es que puedas responder rápido: ¿qué pidió el cliente?, ¿a qué host?, ¿qué upstream atendió?, ¿cuánto tardó?, ¿hubo redirección?, ¿qué status devolvió?
Ejemplo de directivas típicas (ubicación y nombres pueden variar por distro):
http { access_log /var/log/nginx/access.log; # o por server: # server { access_log /var/log/nginx/mi-sitio.access.log; } }Comandos útiles para analizar en caliente:
- Ver últimas líneas:
tail -f /var/log/nginx/access.log - Filtrar por status:
awk '$9 ~ /^5/ {print}' /var/log/nginx/access.log | tail - Top URLs con 404:
awk '$9==404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head - Top IPs:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
error.log: niveles y cómo usarlos sin “ruido”
El error.log registra eventos internos: errores de permisos, fallos al conectar con upstream, problemas de resolución DNS, conflictos de configuración, etc. Puedes controlar el nivel de detalle con la directiva error_log.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
Niveles comunes (de menor a mayor verbosidad):
emerg,alert,crit: fallos graves (arranque/seguridad/recursos).error: errores que impiden completar una solicitud (muy útil en producción).warn: advertencias (posibles problemas).notice,info: información operativa.debug: extremadamente detallado (ideal en laboratorio o con alcance limitado).
Ejemplo:
http { error_log /var/log/nginx/error.log warn; # Para un server específico: # server { error_log /var/log/nginx/mi-sitio.error.log info; } }Consejo práctico: en producción suele bastar error o warn. Sube temporalmente a info para investigar, y vuelve a bajar para evitar crecimiento de logs.
Crear un log_format útil para diagnóstico
El formato por defecto puede quedarse corto cuando necesitas correlación y tiempos. Un log_format “diagnóstico” suele incluir: host, request_id, upstream, tiempos, status upstream, bytes, referer, user-agent y la URL completa.
Ejemplo de log_format recomendado
http { log_format diag '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' 'host="$host" sni="$ssl_server_name" ' 'ref="$http_referer" ua="$http_user_agent" ' 'rt=$request_time ' 'uaddr="$upstream_addr" ' 'ustatus="$upstream_status" ' 'urt="$upstream_response_time" ' 'xff="$http_x_forwarded_for" ' 'rid="$request_id"'; access_log /var/log/nginx/access.log diag; }Notas prácticas:
$request_timemide el tiempo total de Nginx para esa solicitud.$upstream_response_timeayuda a separar “Nginx lento” vs “backend lento”.$upstream_statuspermite ver si el 502/504 viene del upstream o de Nginx.$request_ides muy útil para correlación; si no lo tienes disponible, puedes añadirlo como cabecera hacia el backend y devolverlo al cliente (según tu arquitectura).
Separar logs por server o por tipo de tráfico
En entornos con varios sitios, separar logs reduce el tiempo de diagnóstico:
server { server_name app.ejemplo.com; access_log /var/log/nginx/app.access.log diag; error_log /var/log/nginx/app.error.log warn; # ... }También puedes desactivar logs de rutas ruidosas (por ejemplo, health checks) para mejorar señal/ruido:
location = /health { access_log off; return 200; }Técnicas de troubleshooting: guía paso a paso
Cuando algo falla, evita cambios impulsivos. Sigue un orden: validar configuración, confirmar que Nginx está escuchando, revisar DNS/host, verificar permisos/rutas, y luego revisar upstreams.
1) Validar configuración antes de recargar
Siempre valida sintaxis y coherencia:
nginx -tPara ver el archivo final “expandido” (útil con include):
nginx -T | lessQué buscar:
- Errores de sintaxis o llaves mal cerradas.
- Directivas duplicadas o en contexto incorrecto.
- Conflictos de
server_nameolistenque causen que el tráfico caiga en el server equivocado.
2) Confirmar que Nginx está escuchando en el puerto esperado
Si el sitio “no abre”, confirma puertos:
ss -lntp | grep nginxVerifica que exista un listen 80; o listen 443 ssl; donde corresponde, y que no haya otro proceso ocupando el puerto.
3) Revisar DNS y server_name (y el “default_server”)
Problemas típicos:
- DNS apunta a otra IP.
- El
server_nameno coincide con el Host que llega, y Nginx usa el server por defecto. - Hay múltiples
servercon el mismoserver_namey mismolisten, generando comportamiento inesperado.
Comprobaciones rápidas:
- Resolver DNS:
dig +short app.ejemplo.com - Probar Host explícito contra una IP:
curl -I http://IP_DEL_SERVIDOR -H 'Host: app.ejemplo.com'
Si sospechas que estás cayendo en el server equivocado, identifica cuál es el default_server para ese puerto y revisa el orden de carga de archivos en sites-enabled (o el mecanismo equivalente en tu sistema).
4) Revisar permisos y propiedad de archivos (403/404 “engañosos”)
Nginx suele ejecutarse como un usuario sin privilegios (por ejemplo www-data o nginx). Para servir archivos estáticos, ese usuario necesita permiso de lectura sobre el archivo y permiso de ejecución (traversal) sobre todos los directorios del camino.
Comandos útiles:
- Ver usuario del worker:
ps aux | grep nginx - Inspeccionar permisos:
namei -l /ruta/al/archivo
5) Conflictos entre server blocks
Señales típicas:
- Un dominio muestra el sitio de otro dominio.
- Redirecciones inesperadas entre hosts.
- Certificado incorrecto en TLS (si aplica) por SNI/host mal resuelto.
Cómo investigarlo:
- Usa
nginx -Ty busca todos losserver_nameylistenrelevantes. - Verifica si hay un
server“catch-all” conserver_name _;odefault_serverque esté capturando tráfico. - Prueba con
curlenviando distintosHosty compara respuestas.
Escenarios guiados de diagnóstico
Escenario A: 403 Forbidden por permisos
Síntoma: el navegador/curl devuelve 403 al pedir un archivo existente.
Paso 1: confirma el 403 en access.log
tail -n 50 /var/log/nginx/access.logBusca la línea con "GET /..." 403.
Paso 2: mira el error.log para la causa exacta
tail -n 100 /var/log/nginx/error.logMensajes típicos:
permission deniedal abrir archivodirectory index of ... is forbidden(no hay index y no se permite listar)
Paso 3: valida permisos en toda la ruta
namei -l /var/www/mi-sitio/public/index.htmlRegla práctica: directorios con x para el usuario/grupo adecuado, archivos con r. Si el usuario de Nginx es www-data, asegúrate de que pueda atravesar directorios y leer archivos.
Paso 4: revisa si el problema es “directory index forbidden”
Si pides / y no existe index.html (o el que tengas configurado), Nginx puede devolver 403. Soluciones típicas: crear el index, ajustar index, o habilitar autoindex solo si realmente lo necesitas (con cuidado).
Escenario B: 404 Not Found por root incorrecto
Síntoma: 404 para archivos que “crees” que existen.
Paso 1: confirma qué location está matcheando
Un 404 puede venir de un location distinto al esperado. Revisa tu configuración efectiva con:
nginx -T | lessPaso 2: revisa el root (o alias) en ese server/location
Error común: confundir root y alias. Con root, Nginx concatena el URI; con alias, reemplaza el prefijo del location.
Ejemplo típico con alias (correcto):
location /static/ { alias /var/www/mi-sitio/static/; }Si en vez de alias usas root aquí, podrías terminar buscando en una ruta distinta a la real.
Paso 3: verifica en disco la ruta final
Si el request es /static/app.css, con el ejemplo anterior Nginx buscará /var/www/mi-sitio/static/app.css. Confírmalo:
ls -l /var/www/mi-sitio/static/app.cssPaso 4: revisa el error.log
Un 404 por ruta suele dejar pistas como open() ... failed (2: No such file or directory).
Escenario C: 502 Bad Gateway por backend caído
Síntoma: Nginx responde 502 al hacer proxy hacia una app.
Paso 1: confirma el 502 y mira upstream en access.log
Con un log_format diagnóstico, verás uaddr, ustatus y tiempos. Si no lo tienes, aún puedes ver el 502 en access.log.
Paso 2: revisa error.log para el motivo exacto
Mensajes típicos:
connect() failed (111: Connection refused) while connecting to upstream(backend no escucha)no live upstreams(pool sin destinos disponibles)upstream timed out(backend lento o bloqueado)
Paso 3: prueba conectividad desde el servidor Nginx al backend
curl -i http://127.0.0.1:3000/Si el backend está en otra máquina:
curl -i http://IP_BACKEND:PUERTO/Paso 4: valida que el upstream configurado coincide con la realidad
- ¿El puerto es correcto?
- ¿El backend escucha en
127.0.0.1o en0.0.0.0? - ¿Hay firewall local bloqueando?
Paso 5: si el upstream es por nombre DNS
Si usas un hostname en proxy_pass o en upstream, confirma resolución DNS desde el servidor. Un fallo de DNS puede manifestarse como 502/504 dependiendo del caso:
getent hosts backend.ejemplo.localSi tu arquitectura depende de DNS dinámico, asegúrate de tener una estrategia de resolución adecuada (por ejemplo, resolver cuando aplique) y de entender cuándo Nginx resuelve nombres.
Escenario D: bucles de redirección (too many redirects)
Síntoma: el navegador indica demasiadas redirecciones; en curl -I ves 301/302 repetidos.
Paso 1: reproduce con curl siguiendo redirecciones
curl -I -L http://app.ejemplo.com/Paso 2: identifica el salto que se repite
Observa cabeceras Location. Si ves que vuelve a la misma URL o alterna entre http/https o entre dos hosts, ya tienes la pista.
Paso 3: causas comunes
- Redirección HTTP→HTTPS en Nginx, pero el backend también redirige (o viceversa) y no se respetan cabeceras como
X-Forwarded-Proto. - Reglas de
return 301orewriteque no discriminan correctamente el host o el esquema. - Confusión entre
server_name(www vs no-www) con redirecciones en ambos sentidos.
Paso 4: verifica qué host/esquema cree el backend que está usando
Si hay reverse proxy, revisa que se envíen cabeceras coherentes al backend (por ejemplo, Host y X-Forwarded-Proto) y que el backend esté configurado para confiar en ellas cuando corresponda. Un síntoma típico es: Nginx termina TLS, pero el backend cree que es HTTP y redirige a HTTPS, mientras Nginx ya está en HTTPS y vuelve a redirigir por otra regla.
Paso 5: confirma en logs
- En access.log: secuencia de 301/302 para la misma IP/UA.
- En error.log: normalmente no hay error; es un problema lógico de reglas.
Checklist operativo para cambios seguros en producción
| Fase | Acción | Comando/Verificación |
|---|---|---|
| Preparación | Respaldar configuración actual | cp -a /etc/nginx /etc/nginx.bak.$(date +%F-%H%M) |
| Preparación | Editar cambios de forma atómica | Preferible editar en archivo nuevo y luego reemplazar |
| Prueba | Validar sintaxis y contexto | nginx -t |
| Prueba | Inspeccionar config efectiva si hay includes | nginx -T | less |
| Despliegue | Recargar sin cortar conexiones | nginx -s reload (o el método de tu sistema) |
| Verificación | Probar endpoints críticos | curl -I a URLs clave, con -H 'Host: ...' si aplica |
| Verificación | Revisar que el server correcto atiende | Comparar cabeceras/respuesta esperada por host |
| Verificación | Monitorear logs tras el cambio | tail -f a access/error y buscar 4xx/5xx |
| Rollback | Plan de reversión listo | Restaurar backup y recargar; confirmar con curl |