Resolución de problemas de tiempo de espera de Nginx 504 Gateway Timeout y tiempo de espera del cliente
Domina los tiempos de espera de Nginx, incluido el temido 504 Gateway Timeout, aprendiendo a ajustar directivas de proxy críticas. Esta guía detalla cómo aumentar `proxy_read_timeout`, optimizar el almacenamiento en búfer y usar registros de errores para diagnosticar fallos de comunicación entre Nginx y los servidores upstream para un manejo robusto de conexiones.
Resolución de problemas de tiempo de espera de Nginx 504 Gateway Timeout y tiempo de espera del cliente
Un 504 Gateway Timeout de Nginx significa que Nginx actuaba como proxy o puerta de enlace y no recibió una respuesta del servicio upstream a tiempo. El upstream podría ser una aplicación Node.js, Gunicorn, PHP-FPM, otro servicio de Nginx, una API interna o un balanceador de carga.
La solución tentadora es aumentar cada tiempo de espera a cinco minutos y seguir adelante. A veces, un tiempo de espera más largo es la medida correcta a corto plazo, especialmente para informes, importaciones o trabajos solo de administración. Pero si una solicitud de usuario normal necesita más tiempo del que Nginx permite actualmente, también deberías preguntarte por qué el backend es lento, si la solicitud debería ser asíncrona y si otro proxy en la ruta tiene un tiempo de espera más corto de todos modos.
Entendiendo el error 504 Gateway Timeout
Un error 504 Gateway Timeout ocurre cuando Nginx, actuando como proxy inverso o puerta de enlace, no recibe una respuesta oportuna del servidor upstream al que está reenviando las solicitudes. En términos simples: Nginx le preguntó al backend por una respuesta, esperó la cantidad de tiempo configurada y se rindió porque no llegó ninguna respuesta.
Esto es diferente de un 502 Bad Gateway, donde Nginx recibió una respuesta upstream inválida o cerrada prematuramente, y de un 503 Service Unavailable, que a menudo significa que un servicio no está disponible intencionalmente o está sobrecargado. La distinción importa porque un 504 te señala hacia la espera, el tiempo y la latencia del upstream.
Directivas clave que controlan los tiempos de espera del upstream
Al realizar solicitudes proxy, Nginx utiliza varias directivas críticas, ubicadas principalmente dentro de los bloques http, server o location, o específicamente dentro de un bloque upstream. Ajustar estos valores es el método principal para resolver errores 504.
1. proxy_connect_timeout
Establece el tiempo de espera para establecer una conexión con el servidor upstream. Si Nginx no puede conectarse dentro de este período, devuelve un error de tiempo de espera.
Predeterminado: 60 segundos
proxy_connect_timeout 60s;
2. proxy_send_timeout
Establece el tiempo de espera entre dos operaciones de escritura sucesivas al servidor upstream. Esto es relevante al enviar un cuerpo de solicitud grande.
Predeterminado: 60 segundos
proxy_send_timeout 60s;
3. proxy_read_timeout (La solución más común para 504)
Establece el tiempo de espera para recibir una respuesta del servidor upstream después de que se hayan enviado los encabezados de la solicitud. Si la aplicación backend tarda demasiado en procesar la solicitud y generar un cuerpo de respuesta, esta es la directiva que debe aumentarse.
Predeterminado: 60 segundos
# Ejemplo: Aumentar el tiempo de espera de lectura a 120 segundos para una API lenta
proxy_read_timeout 120s;
Si tu aplicación excede con frecuencia el valor predeterminado, aumenta este valor con precaución y continúa investigando. Un valor muy alto puede mantener abiertas las conexiones del cliente mientras el backend ya no está saludable.
Abordando los tiempos de espera del lado del cliente
Los tiempos de espera del lado del cliente son una falla diferente. El navegador, la aplicación móvil, el balanceador de carga, la CDN o el servicio que llama se rinden antes de que Nginx termine la respuesta. En ese caso, el usuario puede ver un error del navegador o un error de puerta de enlace desde una capa frente a Nginx, mientras que Nginx puede registrar una conexión cerrada en lugar de un 504 limpio.
Si estás experimentando tiempos de espera del cliente antes de que Nginx registre un 504, debes observar la conexión entre el cliente y Nginx.
1. Keepalive del lado del cliente
Si el cliente cierra la conexión prematuramente, Nginx podría recibir un error o el cliente simplemente podría agotar el tiempo de espera esperando datos.
Si el cliente es otro proxy o balanceador de carga, verifica su configuración de tiempo de espera con respecto a Nginx y el backend. El tiempo de espera más corto en la cadena generalmente gana. Un patrón común es: CDN espera 100 segundos, balanceador de carga espera 60 segundos, Nginx espera 180 segundos, el backend tarda 120 segundos. Los usuarios aún fallan a los 60 segundos porque el balanceador de carga se rinde primero.
2. send_timeout de Nginx
Esta directiva controla cuánto tiempo esperará Nginx para que el cliente reconozca o reciba datos (el tiempo entre dos operaciones de escritura sucesivas al cliente).
Predeterminado: 60 segundos
# Establece esto si los clientes están agotando el tiempo de espera mientras Nginx envía la respuesta
send_timeout 120s;
Optimizando el almacenamiento en búfer para respuestas grandes
A veces, el backend comienza a responder, pero la entrega sigue siendo lenta porque la respuesta es enorme, el cliente es lento o Nginx tiene que almacenar en búfer más de lo esperado. Esto es común con exportaciones CSV generadas, descargas de medios enrutadas a través de una aplicación o API que devuelven cargas útiles JSON muy grandes.
Nginx utiliza búferes para retener temporalmente los datos recibidos del upstream antes de enviarlos al cliente. Si la respuesta es muy grande, estos búferes pueden excederse, lo que lleva a un manejo complejo o latencia percibida.
Directivas clave de almacenamiento en búfer
Estas generalmente se establecen dentro del bloque location o server:
| Directiva | Propósito |
|---|---|
proxy_buffers |
Establece el número y tamaño de los búferes utilizados para leer la respuesta del upstream. Formato: número tamaño; |
proxy_buffer_size |
Establece el tamaño del primer búfer, que se utiliza para leer el encabezado de la respuesta. |
proxy_max_temp_file_size |
Si la respuesta excede los búferes disponibles, Nginx escribe en archivos temporales. Esto establece el tamaño máximo para estos archivos temporales. |
Ejemplo de configuración para alto volumen/respuestas grandes:
location /api/heavy_report {
proxy_pass http://backend_app;
# Aumentar el tiempo de espera de lectura
proxy_read_timeout 180s;
# Ajustar el almacenamiento en búfer para cuerpos de respuesta potencialmente grandes
# Usar 8 búferes, cada uno de hasta 1MB (1024k)
proxy_buffers 8 1024k;
proxy_buffer_size 256k;
# Permitir archivos temporales de hasta 500MB si los búferes se desbordan
proxy_max_temp_file_size 500m;
}
Si la respuesta de tu backend es realmente enorme, considera servir un archivo generado desde un almacenamiento de objetos o almacenamiento estático en lugar de mantener la solicitud abierta a través de la aplicación. Para exportaciones, un patrón común es: poner en cola el trabajo, generar el archivo, luego permitir que el usuario lo descargue desde una URL estática cuando esté listo.
Pasos de solución de problemas y análisis de registros
Resolver los tiempos de espera requiere identificar dónde ocurrió la detención: Cliente -> Nginx, o Nginx -> Backend.
Paso 1: Verificar los registros de errores de Nginx
El registro de errores de Nginx es tu fuente definitiva para determinar si Nginx agotó el tiempo de espera esperando al backend.
Busca entradas que contengan frases como:
upstream timed out (110: Connection timed out)upstream prematurely closed connection while reading response header from upstream
Si ves estas, el problema radica en proxy_read_timeout o en el tiempo de procesamiento del backend.
También busca client prematurely closed connection. Eso generalmente significa que el cliente o un proxy frente a Nginx se rindió primero. En ese caso, aumentar proxy_read_timeout por sí solo no ayudará al usuario.
Paso 2: Verificar los registros de la aplicación backend
Si Nginx agota el tiempo de espera (los registros indican 504), verifica inmediatamente los registros del servicio upstream (por ejemplo, registros de PHP-FPM, registros de Gunicorn, registros del servidor de aplicaciones Java). Debes confirmar si la solicitud llegó al backend y cuánto tiempo tomó completarse.
- Si los registros del backend muestran que la solicitud tomó más tiempo que tu
proxy_read_timeoutconfigurado, aumenta el tiempo de espera de Nginx. - Si los registros del backend muestran que la solicitud se completó rápidamente, el problema podría ser la latencia de la red entre Nginx y el backend, o un tiempo de espera del cliente mal configurado frente a Nginx.
Paso 3: Usar el encabezado X-Upstream-Response-Time (Opcional)
Para diagnósticos detallados, puedes registrar el tiempo exacto que el upstream tardó en responder usando la variable $upstream_response_time en tu formato de registro de acceso. Esto ayuda a confirmar el rendimiento real del backend.
En tu nginx.conf:
log_format proxy_detailed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $request_time $upstream_response_time';
access_log /var/log/nginx/access.log proxy_detailed;
Al analizar $upstream_response_time, puedes ver la duración precisa que Nginx esperó, independientemente de la configuración de tiempo de espera de Nginx.
Para una prueba rápida única, llama al upstream directamente desde el host de Nginx:
time curl -sS -o /dev/null -w 'status=%{http_code} total=%{time_total}\n' http://127.0.0.1:3000/slow-route
Si la llamada directa al upstream ya es lenta, Nginx solo está informando el problema. Si la llamada directa es rápida pero la solicitud proxy agota el tiempo de espera, inspecciona la configuración del proxy, la resolución de DNS, la red de contenedores, TLS entre servicios internos u otro salto entre Nginx y la aplicación.
Aplica el cambio útil más pequeño
Una solución de producción razonable a menudo se ve así:
location /api/reports/ {
proxy_pass http://backend_app;
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 180s;
}
Eso aumenta el tiempo de espera de lectura solo para el endpoint de informe lento. No hace que cada solicitud en el sitio espere tres minutos. Para una ruta de inicio de sesión, ruta de pago, verificación de salud o endpoint de API pública, un tiempo de espera largo puede hacer que las fallas sean más dolorosas porque los clientes esperan más tiempo para una solicitud que es poco probable que se recupere.
Para PHP-FPM, el equivalente puede involucrar directivas FastCGI:
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 120s;
include fastcgi_params;
}
Recuerda que PHP, Python, Node.js, servidores de aplicaciones, colas, bases de datos, CDN y balanceadores de carga pueden tener sus propias configuraciones de tiempo de espera. Nginx no puede hacer que un backend continúe funcionando después de que el tiempo de espera del trabajador del backend mate la solicitud.
Después de realizar cualquier cambio de configuración (por ejemplo, aumentar los tiempos de espera o ajustar los tamaños de búfer), siempre prueba la sintaxis de configuración y recarga Nginx:
sudo nginx -t
sudo systemctl reload nginx
Luego observa tanto los registros de Nginx como los del upstream mientras repites la misma solicitud:
sudo tail -f /var/log/nginx/error.log
La mejor solución de tiempo de espera te deja con una razón clara para el cambio: esta ruta legítimamente toma hasta dos minutos, este upstream ahora tiene límites coincidentes y las solicitudes lentas son visibles en los registros o métricas. Cualquier cosa menos es un parche temporal, y los parches temporales deben etiquetarse como tales en tu revisión de configuración o notas de incidentes.