Observabilidad y solución de problemas en Nginx

Capítulo 9

Tiempo estimado de lectura: 10 minutos

+ Ejercicio

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.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

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_time mide el tiempo total de Nginx para esa solicitud.
  • $upstream_response_time ayuda a separar “Nginx lento” vs “backend lento”.
  • $upstream_status permite ver si el 502/504 viene del upstream o de Nginx.
  • $request_id es 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 -t

Para ver el archivo final “expandido” (útil con include):

nginx -T | less

Qué buscar:

  • Errores de sintaxis o llaves mal cerradas.
  • Directivas duplicadas o en contexto incorrecto.
  • Conflictos de server_name o listen que 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 nginx

Verifica 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_name no coincide con el Host que llega, y Nginx usa el server por defecto.
  • Hay múltiples server con el mismo server_name y mismo listen, 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 -T y busca todos los server_name y listen relevantes.
  • Verifica si hay un server “catch-all” con server_name _; o default_server que esté capturando tráfico.
  • Prueba con curl enviando distintos Host y 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.log

Busca la línea con "GET /..." 403.

Paso 2: mira el error.log para la causa exacta

tail -n 100 /var/log/nginx/error.log

Mensajes típicos:

  • permission denied al abrir archivo
  • directory 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.html

Regla 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 | less

Paso 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.css

Paso 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.1 o en 0.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.local

Si 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 301 o rewrite que 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

FaseAcciónComando/Verificación
PreparaciónRespaldar configuración actualcp -a /etc/nginx /etc/nginx.bak.$(date +%F-%H%M)
PreparaciónEditar cambios de forma atómicaPreferible editar en archivo nuevo y luego reemplazar
PruebaValidar sintaxis y contextonginx -t
PruebaInspeccionar config efectiva si hay includesnginx -T | less
DespliegueRecargar sin cortar conexionesnginx -s reload (o el método de tu sistema)
VerificaciónProbar endpoints críticoscurl -I a URLs clave, con -H 'Host: ...' si aplica
VerificaciónRevisar que el server correcto atiendeComparar cabeceras/respuesta esperada por host
VerificaciónMonitorear logs tras el cambiotail -f a access/error y buscar 4xx/5xx
RollbackPlan de reversión listoRestaurar backup y recargar; confirmar con curl

Ahora responde el ejercicio sobre el contenido:

Al investigar un 502 al hacer proxy a una aplicación, ¿qué acción ayuda mejor a determinar si el problema está en el backend y no en Nginx?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

Un 502 suele estar relacionado con el upstream. Revisar error.log muestra la causa (p. ej., connection refused, timed out) y un curl desde el servidor Nginx al backend confirma si el servicio responde y si el puerto/host son correctos.

Siguiente capítulo

Despliegue práctico de una aplicación web detrás de Nginx

Arrow Right Icon
Portada de libro electrónico gratuitaNginx para Principiantes: Domina el Servidor Web Moderno desde Cero
90%

Nginx para Principiantes: Domina el Servidor Web Moderno desde Cero

Nuevo curso

10 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.