
核心要点(TL;DR)
- 502 多为上游进程崩溃或拒连,504 多为上游处理超时,两者根因不同。
- 先看 error_log 中 upstream 关键字,再决定查进程还是查超时。
- proxy_read_timeout 默认 60 秒,长任务接口必须按业务上调。
- upstream 缺 keepalive 会导致端口耗尽,表现为间歇性 502。
凌晨收到监控告警,打开浏览器只看到一行冷冰冰的 502 Bad Gateway,或者页面转圈十几秒后变成 504 Gateway Time-out。这两个错误都发生在 Nginx 反向代理层,但根因完全不同:一个是后端「连不上」,一个是后端「回太慢」。搞混方向,就会在错误的配置项上浪费半小时。本文给出一条从日志判读到配置修复的完整路径,照着做即可。
一、先分清 502 与 504 的本质差异
Nginx 作为反向代理,本身不产生业务响应,它只是把请求转发给 upstream。错误码的含义如下:
| 错误码 | 含义 | 典型根因 |
|---|---|---|
| 502 | 无法从上游获得有效响应 | 后端进程崩溃、端口未监听、连接被拒、返回格式非法 |
| 504 | 上游在规定时间内未响应 | 后端处理慢、数据库锁、proxy_read_timeout 过小 |
一句话记忆:502 查「有没有人接电话」,504 查「接了电话为什么不说话」。
二、第一步永远是读 error_log
不要凭感觉改配置。先复现一次请求,同时盯住错误日志:
tail -f /var/log/nginx/error.log
常见输出与对应方向:
connect() failed (111: Connection refused):后端没起来或端口错了,去查进程。no live upstreams while connecting to upstream:所有节点被健康检查摘除,查后端存活。upstream timed out (110: Connection timed out):连接阶段超时,查网络与 proxy_connect_timeout。upstream timed out (110: Connection timed out) while reading response header:读超时,查业务耗时与 proxy_read_timeout。
日志里会带上 upstream 名称和具体地址,例如 upstream: "http://127.0.0.1:8080",这是后续排查的锚点。
三、验证后端可达性:三条命令定位
拿到上游地址后,在 Nginx 所在主机上直接验证,绕开代理层:
# 1. 端口是否监听 ss -lntp | grep 8080 # 2. 能否直连并拿到响应 curl -v -m 5 http://127.0.0.1:8080/health # 3. 本机连接状态与端口占用 ss -s
判断逻辑:ss 无输出说明后端未启动;curl 秒回但 Nginx 502,检查是否监听在 127.0.0.1 而 Nginx 走容器网桥地址;curl 卡住直到超时,则属于 504 范畴,进入下一节。
若后端是多节点 upstream,逐个 IP 压测,找出坏节点:
for ip in 10.0.0.11 10.0.0.12; do
echo "== $ip"; curl -s -o /dev/null -w '%{http_code} %{time_total}\n' -m 5 http://$ip:8080/health
done四、504 的配置修复:超时参数按业务分层
Nginx 默认 proxy_read_timeout 为 60 秒,长任务接口很容易踩线。修复思路不是全局调大,而是按 location 分层:
location /api/ {
proxy_pass http://backend;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 60s;
}
location /api/export/ {
proxy_pass http://backend;
proxy_read_timeout 300s;
proxy_buffering off;
}同时排查真正的慢源:数据库慢查询、外部接口阻塞、PHP-FPM 进程池打满。用 strace -p <pid> 或应用侧日志确认耗时分布,调大超时只是止血,不是治本。
五、502 的配置修复:连接池与健康检查
间歇性 502 最常见的原因是 upstream 未启用长连接,高并发下大量短连接耗尽本地端口。修复示例:
upstream backend {
server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
keepalive 64;
}
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}要点:keepalive 必须配合 HTTP/1.1 与清空 Connection 头才生效;max_fails 与 fail_timeout 让坏节点自动摘除,避免轮询持续命中故障机器。
另外检查 SELinux:getsebool httpd_can_network_connect,为 off 时 Nginx 无法外连后端,表现为稳定 502,需 setsebool -P httpd_can_network_connect 1 放行。
六、上线前检查清单
- error_log 无 upstream 相关告警,日志级别设为 warn。
- 每个 upstream 节点用 curl 直连返回 200,耗时 < 500ms。
- 长任务接口单独 location,超时与业务匹配。
- upstream 已配置 keepalive、max_fails、fail_timeout。
- SELinux 与防火墙已放行 Nginx 到后端端口。
- 对 502/504 数量建立分钟级监控告警。
在防护层面,可在网关侧对上游健康状态做持续探测,并将异常上游与告警联动,AegisX 的可用性监测能力可覆盖这一环节,缩短从故障发生到定位的时间。
行动建议
现在就做三件事:把 error_log 级别调到 warn 并接入日志平台;为所有 upstream 补上 keepalive 与健康检查参数;按接口耗时分档设置超时。下次再遇到 502 或 504,先看日志再动手,排查时间可以从半小时压缩到三分钟。
常见问题(FAQ)
Nginx 报 502 Bad Gateway,第一步该看哪里?
先查 error_log,而不是改配置。执行 tail -f /var/log/nginx/error.log 复现请求,若出现 connect() failed (111: Connection refused) 说明后端未监听或已崩溃;若出现 no live upstreams 说明健康检查已全部摘除节点。定位到具体 upstream 名称后再去查对应服务进程与端口,比盲目调超时参数高效得多。
504 超时到底是改 proxy_read_timeout 还是 proxy_connect_timeout?
看错误类型。connect timed out 表示 TCP 握手阶段就失败,调 proxy_connect_timeout,通常 5 到 10 秒足够;read timed out 表示已建立连接但后端迟迟不返回,调 proxy_read_timeout。后者常见于报表导出、大文件生成等长任务接口,应按业务最长耗时设置,并配合前端异步轮询,避免无脑设成 600 秒掩盖真实性能问题。
为什么只有部分用户遇到 502,刷新又好?
典型的多节点或连接池问题。若 upstream 配置了多台后端而其中一台异常,轮询命中坏节点时才会 502;若未配置 keepalive,高并发下短连接快速消耗本地端口,也会间歇性失败。用 curl 循环压测每个后端 IP,并用 ss -s 观察 TIME_WAIT 数量即可验证。
上游服务正常,Nginx 仍返回 502 是什么原因?
常见有三类:一是 SELinux 或防火墙拦截了 Nginx 到后端的连接,需 setsebool 放行 httpd_can_network_connect;二是后端只监听 127.0.0.1 而 Nginx 走的是容器或另一网卡地址;三是请求体过大触发 client_max_body_size 限制,返回 413 却被误读。逐项用 curl -v 从 Nginx 主机直连后端验证最直接。
如何避免 502/504 反复出现?
建立三层防护:upstream 配置 max_fails 与 fail_timeout 自动摘除坏节点,加 keepalive 复用连接;对长任务接口单独 location 放宽超时;接入日志告警,对 upstream 错误码做分钟级监控。AegisX 可在网关侧对异常上游状态做持续探测与告警联动,缩短发现到恢复的时间窗口。