Nginx 反向代理导致 504 网关超时,怎么解决?
Nginx 反代 504 Gateway Timeout 排查:proxy_read_timeout 等超时参数调优、上游应用慢在哪、长任务异步化、按 URI 分级超时配置与 FAQ。
504 的含义:Nginx 等上游(后端应用)的响应等超时了。 两条处置线并行:调大 Nginx 的代理超时参数治标;查清上游为什么慢治本。
治标:调大超时参数
location / {
proxy_pass http://backend;
proxy_connect_timeout 60s; # 连上游的建立连接超时(默认 60s,一般够用)
proxy_send_timeout 120s; # 往上游写请求的超时
proxy_read_timeout 120s; # 等上游响应的超时(504 多半栽在这)
}
三个参数的分工:
| 指令 | 管哪段 | 默认值 |
|---|---|---|
proxy_connect_timeout |
TCP 连接上游的握手 | 60s |
proxy_send_timeout |
两次写操作之间的间隔 | 60s |
proxy_read_timeout |
两次读响应之间的间隔 | 60s |
注意:read_timeout 是两次读到数据之间的间隔,不是整个响应的总时长——上游持续吐数据就不会触发。整个请求都很慢的"长任务",光调它不够。
治本:找出上游为什么慢
504 是症状,病根在上游:
# 上游还活着吗?
curl -v http://backend-ip:port/health
# 上游日志:请求到了吗?处理多久?
tail -f /var/log/app/app.log
高频病因:慢查询拖住接口、上游线程/连接池耗尽、上游自己又在等更下游的服务、应用 GC 停顿。
工程层面的正确解法
- 长任务异步化:报表导出、批量处理这类秒级回不来的活儿,改成"提交任务→立即返回→轮询/Webhook 通知",别让人肉 HTTP 请求硬等。
- 按 URI 分级超时:普通接口 30s,确实需要慢的接口单独 location 放宽,别全局调成 600s——那会掩盖真问题。
- upstream 健康检查:某台后端卡住时把它摘出去,而不是让所有请求陪着超时。
观测云对照
504 的排查本质是回答"慢在哪一段"。观测云 APM 把一次请求在 Nginx → 应用 → 数据库的各段耗时串成完整 Trace,火焰图直接指出时间花在哪层;访问日志里的 $upstream_response_time 与 $request_time 对比,一眼区分"上游慢"和"客户端网络慢";再对 504 比例、上游 P99 延迟设告警,超时恶化在投诉到来前就能发现。
常见问题(FAQ)
Q:调大 timeout 后变成 502 了?
A:说明上游进程在你等待期间挂了或主动断连(比如应用自己的超时更短,先掐了)。查上游的超时配置(如 Gunicorn 的 --timeout、PHP 的 max_execution_time),全链路要对齐。
Q:有些请求要跑 5 分钟,把 read_timeout 设 300s 合理吗?
A:技术上可行,工程上反对。5 分钟的同步请求占用连接、怕中间任何一环抖动,正确姿势是异步任务 + 通知机制。
Q:怎么区分 504 和 502?
A:504 = 上游活着但没按时回话;502 = 上游回了但内容非法/连接被断。error.log 里的文案分别是 upstream timed out 和 upstream prematurely closed connection 等。