Nginx 502/504 故障排查步骤与上游超时调优实战 核心要点图解,502 多为上游拒绝连接或进程崩溃,504 多为上游响应超时,先看 error.log 再动手。;调大 proxy_read_timeout 只是止血,根因常在应用慢查询或后端连接池耗尽。;上游启用 keepalive 连接池可显著减少 502,需同时配置 upstream 与 proxy_http_version 1.1。;超时参数应分层设置:网关层大于应用层,避免 Nginx 先于后端断开造成假 502。
Nginx 502/504 故障排查步骤与上游超时调优实战 · 核心要点一图读懂(AegisX)

核心要点(TL;DR)

  • 502 多为上游拒绝连接或进程崩溃,504 多为上游响应超时,先看 error.log 再动手。
  • 调大 proxy_read_timeout 只是止血,根因常在应用慢查询或后端连接池耗尽。
  • 上游启用 keepalive 连接池可显著减少 502,需同时配置 upstream 与 proxy_http_version 1.1。
  • 超时参数应分层设置:网关层大于应用层,避免 Nginx 先于后端断开造成假 502。

线上突然一片 502 Bad Gateway 或 504 Gateway Timeout,第一反应往往是重启 Nginx,但重启只能掩盖问题。真正要做的,是从 error.log 里读出上游到底发生了什么,再决定是改配置、扩进程还是优化应用。本文按「定位—验证—调优—防复发」的顺序给出可直接照做的排查步骤与命令清单。

一、先分清 502 与 504 的成因差异

两者都表示 Nginx 作为反向代理没能从上游拿到有效响应,但失败环节不同:

状态码典型日志关键字根因方向
502connect() failed、Connection refused、upstream prematurely closed上游未监听、进程崩溃、连接被复用后关闭、socket 权限
504upstream timed out (110: Connection timed out)上游处理慢、连接池耗尽、外部依赖阻塞

先执行下面的命令,把最近的真实错误抓出来:

tail -n 200 /var/log/nginx/error.log | grep -E "502|504|upstream"
# 统计近一小时错误类型分布
grep -E "upstream" /var/log/nginx/error.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head

日志中的 upstream: "http://127.0.0.1:9000" 字段直接告诉你该去查哪个后端,不要跳过这一步。

二、验证上游连通性与进程状态

确认目标地址后,从 Nginx 所在机器直接探测,排除网络与权限因素:

  1. 检查端口监听:ss -lntp | grep 9000,确认进程存在且监听在预期地址。
  2. 直连测试:curl -v -m 5 http://127.0.0.1:9000/health,观察是连接被拒还是超时。
  3. 若是 Unix socket,确认权限:ls -l /run/php-fpm.sock,Nginx worker 用户需有读写权限。
  4. 查看后端进程数与资源:ps aux | grep php-fpm | wc -l、dmesg | grep -i oom,排除被 OOM Killer 杀掉。

如果 curl 通而 Nginx 报 502,重点检查 upstream 中配置的地址、DNS 解析结果是否指向旧 IP,以及 SELinux 是否拦截了 httpd_can_network_connect。

三、定位 504:是连接慢还是响应慢

504 需要区分三个超时点,对应不同参数:

  • 连接慢:proxy_connect_timeout,通常 5s 足够,调大意义有限。
  • 发送慢:proxy_send_timeout,请求体大或客户端上传慢时才会触发。
  • 读取慢:proxy_read_timeout,绝大多数 504 出在这里。

进一步确认上游是否真的在干活,可临时抓包或看后端访问日志时间戳:

# 观察上游是否返回数据
tcpdump -i lo -nn port 9000 -c 20
# 检查 PHP-FPM 慢日志
grep -E "pool|max_children" /var/log/php-fpm/www-error.log | tail

若后端日志显示请求早已完成而 Nginx 仍超时,多半是响应体过大、中间链路 MTU 或 keepalive 复用异常,而非应用慢。

四、上游超时与连接池调优实操

推荐的 upstream 配置示例如下,注意 keepalive 三件套必须配套写:

upstream backend {
    server 127.0.0.1:9000 max_fails=3 fail_timeout=10s;
    keepalive 32;
    keepalive_timeout 60s;
}

location / {
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_connect_timeout 5s;
    proxy_send_timeout 30s;
    proxy_read_timeout 60s;
    proxy_next_upstream error timeout http_502 http_504;
    proxy_pass http://backend;
}

调优要点:keepalive 取值建议为后端 worker 数的 1~2 倍且不超过其连接上限;proxy_read_timeout 应略大于应用自身超时,形成「内层先超时、外层后兜底」的层次;proxy_next_upstream 可在多台上游时自动重试,但涉及非幂等请求要谨慎开启。

防护建议:在网关层对异常上游响应做统一拦截与限速,可显著降低故障扩散面。AegisX 的网站防火墙与反向代理防护能力支持对异常状态码与高频重试行为进行识别与处置,可作为 Nginx 前置的一道缓冲。

五、上线前检查清单

  • error.log 与 access.log 已开启并配置轮转,保留至少 7 天。
  • upstream 中 keepalive 与 proxy_http_version 1.1、Connection "" 同时存在。
  • Nginx 超时 > 应用超时 > 数据库/外部调用超时。
  • 后端健康检查接口可用,且 max_fails、fail_timeout 已设置。
  • 变更后执行 nginx -t 并灰度 reload,观察 5xx 比例与 P99 延迟。

行动建议:把上述日志检索命令固化成排障脚本,故障时先跑脚本再改配置;每次调整超时参数都记录变更前后的 5xx 率,用数据判断是否真正解决了上游超时问题,而不是把 504 拖成了更长的等待。

常见问题(FAQ)

Nginx 报 502 Bad Gateway,但后端服务看着是正常的,怎么查?

先确认 Nginx 访问的是哪个上游地址与端口,再用 curl 从 Nginx 所在机器直连该地址验证。若 curl 通而浏览器 502,多为 upstream 配置的 server 与实际监听不一致、DNS 解析到旧 IP,或 SELinux/防火墙拦截。查看 error.log 中 connect() failed 的具体 errno 可快速区分连接被拒、超时还是权限问题。

504 Gateway Timeout 调大 proxy_read_timeout 就能解决吗?

不一定。调大超时只能让慢请求不被提前掐断,若后端本身处理需要几十秒,用户侧体验依然很差。正确做法是先确认是连接慢、发送慢还是读取慢,再针对性调 proxy_connect_timeout 或 proxy_read_timeout,同时排查数据库慢查询、外部 API 阻塞与 PHP-FPM 进程数不足等根因。

为什么加了 upstream keepalive 之后 502 反而变多?

常见原因是只写了 keepalive 指令,却未设置 proxy_http_version 1.1 和 proxy_set_header Connection "",导致长连接未生效或复用到已被后端关闭的连接。此外 keepalive 数量应小于后端 worker 连接上限,否则空闲连接被后端主动断开,Nginx 复用时即报 upstream prematurely closed connection。

PHP-FPM 场景下 502 和 504 分别对应什么问题?

502 通常是 PHP-FPM 进程池耗尽、进程崩溃或 socket 权限不对,Nginx 连不上 fastcgi 后端;504 则是请求已送达但脚本执行超过 fastcgi_read_timeout。排查时先看 php-fpm 慢日志与 pm.max_children 使用率,再核对 request_terminate_timeout 与 Nginx 超时的先后顺序。

如何避免超时参数互相打架导致误判?

遵循外层大于内层的原则:Nginx 的 proxy_read_timeout 应略大于后端应用超时,应用超时又应大于数据库查询超时。这样超时总是由最内层先触发并留下清晰日志,避免 Nginx 先断开连接、后端仍在写响应而产生大量 499 与假 502。