LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

生产环境Nginx常见502、504故障排查全指南

admin
2026年11月13日 7:55 本文热度 179

凌晨被叫醒,打开监控:Nginx 大面积 502。你重启、回滚、拜佛,最后发现只是 PHP-FPM 的 pm.max_children 被打满了。502/504 的可怕之处在于原因藏在 Nginx 身后——它只是个传话的,真正的凶手在后端。本文用故障树把 502/504 的所有常见分支逐层拆开,每个分支附真实错误日志片段、根因和解法,让你下次 30 秒定位,而不是 30 分钟瞎猜。

一、先分清:502 和 504 是两类完全不同的病

状态码含义一句话根因典型日志关键词
502 Bad GatewayNginx 联系不上后端,或后端的回答没法理解连接层就失败了connect() failed / prematurely closed / too big header
504 Gateway TimeoutNginx 联系上了后端,但后端超时没回话后端处理太慢upstream timed out

这个区分决定了排查方向:502 查"连接"(进程在不在、端口通不通、权限够不够),504 查"速度"(慢查询、死锁、资源耗尽)。用 504 的思路去修 502,修到天亮也没用。

二、故障树总览

拿到故障先别急着动手,对着这棵树走一遍:

用户报障:5xx
│
├─ 看 Nginx error.log 的第一行关键词
│
├─ "connect() failed (111: Connection refused)"
│   └─ 502-A:后端进程挂了 / 端口不对 / 监听地址错(§三)
│
├─ "connect() to unix:...sock failed (13: Permission denied)"
│   └─ 502-B:PHP-FPM socket 权限问题(§四)
│
├─ "upstream prematurely closed connection"
│   └─ 502-C:后端收下了连接但主动掐断(§五)
│
├─ "upstream sent too big header"
│   └─ 502-D:响应头超过 Nginx 缓冲区(§六)
│
├─ "no live upstreams"
│   └─ 502-E:upstream 里所有节点都被标记下线(§七)
│
└─ "upstream timed out (110: Connection timed out)"
    └─ 504:后端太慢 / 超时参数太短(§八)

第一动作永远是同一句话:

tail -100 /var/log/nginx/error.log

error.log 里 while connecting to upstream 或 while reading response header from upstream 这半句,就是故障树的分叉口。

三、502-A:Connection refused —— 后端根本没在听

日志现场:

2026/10/19 03:12:05 [error] 1234#1234: *567 connect() failed (111: Connection refused)
while connecting to upstream, client: 10.0.0.88, server: api.example.com,
upstream: "http://127.0.0.1:8080/login", host: "api.example.com"

根因(按概率排序):

  1. 后端进程挂了。Java OOM、PHP-FPM 崩溃、Node 进程退出——最常见,没有之一;

  2. 端口写错。后端实际监听 8081,proxy_pass 写的 8080;

  3. 监听地址错。后端 bind 在 127.0.0.1,Nginx 却通过内网 IP 访问(或反之)。

30 秒定位法:

# ① 端口到底谁在听
ss -lntp | grep-E'8080|8081'

# ② 直接绕过 Nginx 打后端
curl-v http://127.0.0.1:8080/login

# ③ 后端进程还活着吗
ps aux | grep-E'java|php-fpm|node' | grep-vgrep

解法:后端挂了就按后端的方式排查(看应用日志、OOM killer 记录 dmesg | grep -i kill);端口/地址错了改 proxy_pass。根治靠进程守护:systemd 的 Restart=always、supervisor、或 K8s 的 livenessProbe,别靠人肉重启。

四、502-B:Permission denied —— PHP-FPM 的 socket 权限

日志现场:

2026/10/19 03:15:22 [crit] 1234#1234: *890 connect() to unix:/run/php-fpm/www.sock
failed (13: Permission denied) while connecting to upstream,
upstream: "fastcgi://unix:/run/php-fpm/www.sock:"

根因:Nginx 的 worker 以 nginx(或 www-data)用户运行,而 PHP-FPM 的 socket 文件属主和权限不允许它访问。常见于:PHP-FPM 重装后配置重置、有人手改了 socket 目录权限。

解法,改 PHP-FPM 的 pool 配置(如 /etc/php-fpm.d/www.conf):

listen = /run/php-fpm/www.sock
listen.owner = nginx        ; socket 属主 = Nginx 运行用户
listen.group = nginx
listen.mode = 0660

重启 PHP-FPM 后验证:

ls-l /run/php-fpm/www.sock
# srw-rw---- 1 nginx nginx ... www.sock
sudo-u nginx curl--unix-socket /run/php-fpm/www.sock http://localhost/  # 以 nginx 身份试连

顺带一个孪生坑:日志是 (2: No such file or directory) 而不是 Permission denied——socket 文件不存在,说明 PHP-FPM 根本没启动,或 listen 路径和 Nginx 的 fastcgi_pass 不一致。

五、502-C:upstream prematurely closed connection —— 后端主动掐线

日志现场:

2026/10/19 03:21:40 [error] 1234#1234: *1024 upstream prematurely closed connection
while reading response header from upstream, upstream: "http://127.0.0.1:8080/api/order"

翻译:TCP 连接建上了,Nginx 也把请求发过去了,后端处理到一半把连接断了。这是后端"内伤"的信号,常见根因:

  1. 后端进程崩溃:处理这个请求时 panic / OOM / 段错误,连接随进程死亡被内核关闭;

  2. 后端自己的超时更短:比如 Node 默认 server.timeout = 120s(老版本),请求处理超过后主动断开——Nginx 看到的就是"prematurely closed";

  3. PHP-FPM 的 request_terminate_timeout 到期:PHP 脚本跑太久被 FPM 杀掉;

  4. 请求体过大被后端拒收:Tomcat 的 maxPostSize、Express 的 body-parser limit。

定位:这种情况 Nginx 日志只能告诉你"被掐了",凶手名在后端日志里:

# 按故障时间点翻后端日志
journalctl -u php-fpm --since"03:20"--until"03:23"
grep"03:21" /var/log/php-fpm/www-error.log
dmesg | grep-i oom          # 是不是被 OOM killer 干掉的

解法对应:崩溃就修代码/扩内存;超时不对齐就统一链路超时(见 §八);PHP 长任务调大 request_terminate_timeout。

六、502-D:upstream sent too big header —— 缓冲区装不下

日志现场:

2026/10/19 03:30:11 [error] 1234#1234: *2048 upstream sent too big header
while reading response header from upstream

根因:后端返回的响应头超过了 Nginx 缓冲区。几乎都是巨型 Cookie(塞了 JWT + 用户画像 + 埋点信息)或后端框架输出超长自定义头。FastCGI 和 HTTP 代理各有一套缓冲参数,报错哪个就调哪个:

# HTTP 代理(proxy_pass)
proxy_buffer_size 16k;
proxy_buffers8 16k;
proxy_busy_buffers_size 32k;

# FastCGI(PHP-FPM)
fastcgi_buffer_size 16k;
fastcgi_buffers8 16k;
fastcgi_busy_buffers_size 32k;

注意:调大缓冲区是止血,治本要查后端为什么输出这么大的头——把 session 数据塞进 Cookie 是常见坏味道,header 超过 8K 基本就是设计问题。

七、502-E:no live upstreams —— 全员被"标记下线"

日志现场:

2026/10/19 03:40:02 [error] 1234#1234: *4096 no live upstreams
while connecting to upstream

根因:upstream 里有多个节点,Nginx 默认的健康策略(max_fails=1 fail_timeout=10s)把所有节点都在过去 10 秒内标记为失败——哪怕它们只是偶发抖动了一下。于是新请求连试都不试,直接 502。

解法:放宽熔断参数 + 给重试留后路:

upstreambackend {
    server 10.0.0.21:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.22:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.23:8080 backup;        # 兜底节点,全员挂时才启用
}

location / {
    proxy_passhttp://backend;
    proxy_next_upstreamerror timeouthttp_502 http_503;  # 允许换节点重试
    proxy_next_upstream_tries 2;
    proxy_next_upstream_timeout 5s;      # 重试总预算,防雪崩
}

注意 proxy_next_upstream 默认不含 504(怕重试导致后端更崩),也别对非幂等的 POST 请求乱开重试——重复下单的锅比 502 大得多。

八、504:upstream timed out —— 联系上了,但后端太慢

日志现场:

2026/10/19 04:02:17 [error] 1234#1234: *8192 upstream timed out (110: Connection timed out)
while reading response header from upstream, upstream: "http://127.0.0.1:8080/report/export"

根因两条线:

① 后端真的慢。 报表导出、没加索引的慢查询、循环里调外部接口——请求处理时间超过了 Nginx 的耐心。证据在后端:慢查询日志、APM 链路追踪。

② Nginx 超时参数太短。 默认 proxy_read_timeout 60s,正常业务偶尔要跑 2 分钟的接口就会被冤杀。

解法,先把超时参数家族认全(很多人只知道一个):

# HTTP 代理场景
proxy_connect_timeout 10s;     # 连上后端的超时(连不上走这里,别设长)
proxy_send_timeout 60s;        # 把请求发给后端的超时
proxy_read_timeout 120s;       # 等后端响应的超时(504 的命门)

# FastCGI 场景
fastcgi_connect_timeout 10s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 120s;     # 对应 PHP-FPM

但调大超时是最后的手段。正确的优先级:

  1. 能异步就异步:报表导出改成"提交任务→轮询/推送结果",别让用户干等 HTTP;

  2. 能优化就优化:慢查询加索引、N+1 改批量——后端 200ms 能搞定的,不该让 Nginx 等 120s;

  3. 必须长的才调长:个别接口单独 location 里放宽 proxy_read_timeout,别在全局调大——全局长超时会让慢请求堆积,耗尽 worker 连接数,把局部问题拖成全站雪崩。

整条链路的超时要对齐:Nginx 120s,但前端的 CDN 或 LB 只有 60s,那 61 秒的请求照样死在半路。从客户端到应用,逐跳核对。

九、30 秒定位流程

把前面的故障树压缩成一张可执行的排查动线,贴在工位上:

第 1 步(5 秒):tail error.log,抓关键词
       │
第 2 步(10 秒):关键词对号入座
       ├─ refused        → ss -lntp 查端口 → 起后端/改配置
       ├─ denied / sock  → ls -l 查权限 → 改 listen.owner
       ├─ prematurely    → 翻后端日志/dmesg → 崩溃或超时不匹配
       ├─ too big header → 调 proxy/fastcgi buffer,查巨型 Cookie
       ├─ no live        → 查各节点健康 → 调 max_fails/backup
       └─ timed out      → 后端慢日志 → 异步化/调 proxy_read_timeout
       │
第 3 步(15 秒):curl 绕过 Nginx 直打后端验证
       curl -v http://后端:端口/路径    # 通 → 问题在 Nginx 层
                                       # 不通 → 问题在后端层

十、检查清单

故障修复后,用这份清单做复盘,防复发:

  • error.log 里提取了带时间戳的日志片段并归档(复盘证据)

  • 后端进程有自动拉起机制(systemd Restart / supervisor / livenessProbe)

  • PHP-FPM socket 属主与 Nginx 运行用户一致(0660)

  • 链路超时已对齐:CDN → LB → Nginx → 后端,逐跳核对

  • proxy/fastcgi buffer 按业务最大响应头留足余量

  • upstream 多节点配置了合理的 max_fails + backup 兜底

  • 长任务接口已异步化或单独 location 放宽超时

  • proxy_next_upstream 未对非幂等请求开放重试

  • 502/504 监控告警接的是比例(如 5xx > 1%)而非绝对值

十一、总结

502/504 排查的本质是读懂 Nginx 的"转述":它是网关不是凶手,error.log 里 while ... upstream 的每一个关键词都在指认真正的案发现场。记住两条主线——502 查连接、504 查速度,再配合故障树逐层排除,绝大多数故障 30 秒内就能锁定方向。

最后一条建议:把这份故障树存进团队的运维 Wiki,第一次用它是查故障,第十次用它就是给新人讲课的教材。如果这篇帮你少熬了一个夜,欢迎转发给同样在凌晨三点盯过 error.log 的朋友。


阅读原文:原文链接​


该文章在 2026/11/13 13:06:11 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-1  粤公网安备44030602007207号