← 中文资源

简体中文版本

nginx 504 — upstream 读取响应头超时

绕过 nginx 直接测量后端、核对 proxy 超时与连接状态,定位 upstream timed out 的真正原因,而不是一味调大超时。

阅读英文原文

现象

客户端收到 504;错误日志出现 upstream timed out (110: Connection timed out) while reading response header from upstream;健康检查正常但慢接口失败;后端稍后打印了请求日志,而客户端早已收到 504。

诊断

先读错误日志,确认超时发生在连接、发送还是读取阶段:

tail -n 200 /var/log/nginx/error.log | grep -E 'upstream timed out|no live upstreams|connect\(\) failed'
nginx -T 2>/dev/null | grep -nE 'proxy_(read|connect|send)_timeout|proxy_next_upstream'

while reading response header 指向应用响应慢,while connecting 指向不可达或 accept 队列满。默认 proxy_read_timeout 是 60 秒,注意匹配 location 的实际覆盖值。再绕过 nginx 直连后端测 TTFB:

curl -o /dev/null -s -w 'connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}' http://127.0.0.1:3000/slow-endpoint

TTFB 超过超时值即可确认是后端问题;直连很快则查 nginx 配置、DNS 与网络。用 ss -ltnp | grep ':3000' 和 established 连接数判断后端是否饱和。

根因

nginx 在 proxy_read_timeout 内没有收到上游响应头,于是返回 504 并关闭客户端连接。上游请求可能仍在执行,也可能在客户端放弃之后才完成。

修复

先修慢处理或外部依赖,再按 location 设置合理预算,并开启 upstream keepalive:

upstream app_backend {
    server 127.0.0.1:3000;
    keepalive 32;
}

location /reports/ {
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_connect_timeout 2s;
    proxy_read_timeout 120s;  # 仅用于确实需要长预算的接口
    proxy_next_upstream error timeout;
    proxy_pass http://app_backend;
}

proxy_next_upstream 只适合幂等请求,POST 重试可能重复写入。真正耗时的导出应改为后台任务加状态查询,避免长期占用 worker 连接。

验证

重放原请求应在预算内返回 200,错误日志不再新增 upstream timed out;压测下 TTFB P95 低于 proxy_read_timeout,到上游的 established 连接数有界。