Nginx 502 Bad Gateway的完整排查指南(PHP-FPM / 上游 / 超时三类原因全覆盖)

 更新时间:2026年11月04日 09:13:56   作者:snysy  
还在为网站偶尔蹦出502错误头疼吗,本文手把手教你从Nginx错误日志入手,快速定位PHP-FPM进程满、socket路径不一致、后端超时等常见原因,并提供max_children计算、慢日志排查和监控告警等实用方案,帮你彻底告别502,需要的朋友可以参考下

本文为个人运维实操笔记,记录一次线上 502 的完整排查过程,以及后续整理的通用排查清单。文中命令均在 Debian 12 + Nginx 1.24 + PHP-FPM 8.2 环境验证过,可直接复制使用。

一、测试环境说明

  • 服务器:1 核 1G Linux VPS(香港 CN2 节点,LuckVM),Debian 12
  • Web 环境:Nginx 1.24 + PHP-FPM 8.2 + MySQL 8.0
  • 站点程序:WordPress 6.x(宝塔面板部署)

说明一下我这台的背景:这是一个日访问量不大的个人博客,平时很稳,突然某个下午开始间歇性出现 502,刷新几次又能打开,属于典型的「偶发 502」。偶发比必现更难查,因为它考验的是排查思路,而不是运气。

二、先搞清楚 502 到底是谁报的

很多人一看到 502 就以为是 Nginx 挂了,其实绝大多数时候 Nginx 好好的,是它背后的「上游」出了问题。

502 Bad Gateway 的含义是:Nginx 作为反向代理,从「上游」(upstream)拿到了一个无效的响应,或者根本连不上上游。所以排查的第一步,永远是确定上游是谁。

2.1 判断上游类型

看站点的 Nginx 配置(一般在这几个位置):

# 宝塔面板的站点配置
ls /www/server/panel/vhost/nginx/
# 原生安装一般在
ls /etc/nginx/conf.d/
ls /etc/nginx/sites-enabled/

打开对应站点的 conf,找 location ~ \.php$ 或 proxy_pass:

location ~ \.php$ {
fastcgi_pass unix:/tmp/php-cgi-82.sock; # 上游是 PHP-FPM
...
}
location /api/ {
proxy_pass http://127.0.0.1:8080; # 上游是后端应用
...
}
  • fastcgi_pass 指向 PHP-FPM → 问题多半在 PHP-FPM
  • proxy_pass 指向后端服务 → 问题多半在 那个后端服务

我这台是 WordPress,上游就是 PHP-FPM,fastcgi_pass unix:/tmp/php-cgi-82.sock。

2.2 看错误日志先定位

**不要一上来就瞎猜,先看日志。**两个地方都要看:

# Nginx 错误日志(宝塔在 /www/wwwlogs/)
tail -n 100 /www/wwwlogs/你的域名.error.log
# 或原生安装
tail -n 100 /var/log/nginx/error.log
# PHP-FPM 慢日志 / 错误日志
tail -n 100 /www/server/php/82/var/log/php-fpm.log

我当时的 error.log 里反复出现这一行:

2026/xx/xx 14:32:11 [error] 12345#0: *67890 connect() to unix:/tmp/php-cgi-82.sock failed (11: Resource temporarily unavailable) while connecting to upstream

关键信息是 Resource temporarily unavailable,这是最典型的 PHP-FPM 连接数被占满的报错,对应 PHP-FPM 的 listen.backlog 溢出或 pm.max_children 不够用。

到这里,方向就明确了:不是 Nginx 的锅,是 PHP-FPM 顶不住并发。

三、按错误信息对号入座

我把常见的 502 报错和对应原因整理成一张对照表,方便直接定位:

error.log 关键信息含义大概率原因
connect() to ... failed (11: Resource temporarily unavailable)连接上游失败,资源暂时不可用PHP-FPM 进程数满了 / backlog 溢出
connect() to ... failed (2: No such file or directory)找不到 socket 文件PHP-FPM 没启动,或 socket 路径配置不一致
connect() to ... failed (111: Connection refused)拒绝连接上游服务挂了,或没在监听该端口
upstream timed out (110: Connection timed out)上游响应超时后端处理太慢(数据库慢查询、死循环等)
recv() failed (104: Connection reset by peer)被上游重置PHP 进程崩溃 / 被 OOM Killer 杀掉
no live upstreams没有可用上游所有后端节点都挂了

有了这张表,配合日志里的关键词,基本能锁定大方向。

四、PHP-FPM 类 502 的排查与解决

回到我的案例,报错是 Resource temporarily unavailable,按下面几步走。

4.1 先看 PHP-FPM 进程状态

# 看 php-fpm 是否在跑
systemctl status php-fpm-82 # 宝塔的写法
# 或
systemctl status php8.2-fpm
# 看当前有多少个 php-fpm 子进程
ps -ef | grep php-fpm | grep -v grep | wc -l
# 更直观地看进程池状态
ps -ef | grep 'php-fpm: pool'

我当时用 ps 一数,子进程数量正好卡在 10 个,也就是 pm.max_children = 10 被占满了——并发一上来,没有空闲 worker 接活,新连接就进不来,直接 502。

4.2 看 PHP-FPM 配置

宝塔的配置在 /www/server/php/82/etc/php-fpm.d/www.conf,原生一般在 /etc/php/8.2/fpm/pool.d/www.conf:

grep -E 'pm\.|listen\.' /www/server/php/82/etc/php-fpm.d/www.conf

关注这几个参数:

pm = dynamic
pm.max_children = 10 # 最关键:最大子进程数
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
listen.backlog = 511 # 排队的连接上限

4.3 max_children 到底该设多少

不能拍脑袋写大,因为它直接决定内存占用。 计算方法是:

pm.max_children = 可用内存 / 单个 PHP 进程平均占用内存

先测单个 PHP 进程吃多少内存:

# 找一个正在运行的 php-fpm 子进程,看它的 RSS
ps -eo pid,rss,cmd | grep 'php-fpm: pool' | awk '{print $2/1024" MB"}' | sort -n | tail -5

假设单个进程平均 60 MB,服务器是 1G 内存,扣掉系统、Nginx、MySQL 占的约 400 MB,剩 600 MB 给 PHP-FPM:

600 / 60 = 10

所以我这台算出来就是 10,正好卡满,难怪一有并发就崩。解决思路不是无脑加大,而是:① 适当加大但留够余量;② 更要紧的是找出为什么单个进程吃内存/耗时长。

我最后是先把 max_children 调到 16,同时开慢日志找慢请求,双管齐下。

4.4 开启 PHP-FPM 慢日志找慢请求

很多 502 的根因是「个别请求特别慢,把 worker 长期占住」。开慢日志就能揪出来:

在 www.conf 里加:

request_slowlog_timeout = 5s
slowlog = /www/server/php/82/var/log/php-fpm-slow.log

重启后,tail -f 慢日志,就能看到卡在哪一行代码:

tail -f /www/server/php/82/var/log/php-fpm-slow.log

我这边揪出来的慢请求集中在 WordPress 的插件搜索,日志里能看到卡在 wp_posts 表的 LIKE '%关键词%' 查询上。给搜索字段加索引、装个缓存插件后,慢请求基本消失。

4.5 backlog 太小也会 502

listen.backlog 是内核层面允许排队的连接数。并发一高、worker 全忙时,超出的连接会进 backlog 队列等着。队列也满了,新连接就被拒,表现同样是 Resource temporarily unavailable。

适当调大:

listen.backlog = 2048

同时确认系统的 somaxconn:

sysctl net.core.somaxconn
# 太小就永久调大
echo 'net.core.somaxconn = 32768' >> /etc/sysctl.conf
sysctl -p

4.6 socket 路径不一致的坑

有一类 502 特别隐蔽:Nginx 里写 fastcgi_pass unix:/tmp/php-cgi-82.sock,但 PHP-FPM 实际监听的 socket 路径是另一个(比如升级 PHP 版本后路径变了),两边对不上,Nginx 就永远连不上。

# 看 PHP-FPM 实际监听的是哪个 socket
grep '^listen' /www/server/php/82/etc/php-fpm.d/www.conf
# 看这个 socket 文件到底存不存在
ls -l /tmp/php-cgi-82.sock

两边必须完全一致。这个坑我在升级 PHP 版本时踩过一次,改了 Nginx 配置里的版本号才对上。

五、超时类 502 的排查

如果日志是 upstream timed out,方向就不在进程数,而在后端处理太慢。

5.1 先确认超时阈值

location ~ \.php$ {
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s; # 最常触发的是这个
...
}

fastcgi_read_timeout 是「等 PHP 返回结果」的最长时间,超过就 502。

5.2 定位到底慢在哪

按 SQL → 代码 → 外部请求的顺序查:

# 1. MySQL 慢查询日志
tail -n 50 /www/server/data/mysql-slow.log
# 2. 实时看 MySQL 当前在跑什么
mysql -uroot -p -e "SHOW FULL PROCESSLIST;"
# 3. 看是不是在等外部接口(比如调用第三方 API 卡住)
strace -p <php-fpm-pid> -e trace=network

我这台的根因就是上面说的慢查询。注意:调大超时只是兜底,不是解药,真正的解法是让后端变快。

六、上游是后端应用(proxy_pass)时的 502

如果站点是反代后端服务(Java/Node/Python),排查思路类似:

# 1. 后端进程还在不在
systemctl status your-app
ps -ef | grep your-app | grep -v grep
# 2. 端口有没有在监听
ss -lntp | grep ':8080'
# 3. 直接本地请求后端,绕过 Nginx,看后端自己是不是正常
curl -v http://127.0.0.1:8080/

如果 curl 本地就报错,那问题 100% 在后端,跟 Nginx 无关。常见原因:应用启动失败、被 OOM Killer 杀掉、连接池耗尽。

6.1 排查是否被 OOM 杀掉

后端连接被重置(Connection reset by peer)时,很可能是进程被系统内存回收杀掉了:

dmesg -T | grep -i 'killed process'
journalctl -k | grep -i oom

如果是 OOM,就得回到「内存调优」那条线去解决(缩小 JVM 堆、限制进程内存、加 swap 等)。

七、通用排查流程图(照着走就行)

整理成一句话版本,方便应急时按顺序走。下面用流程图把「看日志 → 抓关键词 → 对号入座」这条主线画出来,照着走即可:

看到 502
├─ 1. tail -f Nginx error.log,抓关键词
├─ 2. 关键词是 Resource temporarily unavailable
│ → PHP-FPM 进程满:查 pm.max_children、backlog
├─ 3. 关键词是 Connection refused / No such file
│ → 上游没起 / socket 路径不一致:查进程和监听
├─ 4. 关键词是 upstream timed out
│ → 后端慢:查慢查询、慢日志、外部接口
├─ 5. 关键词是 Connection reset by peer
│ → 进程崩溃 / OOM:查 dmesg、journalctl
└─ 6. 无报错但偶发
→ 抓并发高峰时段、开慢日志、看监控

八、怎么让 502 不再复发

解决单次 502 容易,难的是别让它反复出现。我做了这几件事:

1. 加监控告警,不要等用户反馈才知道:

# 简单版:定期本地请求,非 200 就记日志
cat > /root/check_502.sh <<'EOF'
#!/bin/bash
code=$(curl -o /dev/null -s -w '%{http_code}' -m 10 http://127.0.0.1/)
if [ "$code" != "200" ]; then
echo "$(date '+%F %T') 站点异常,状态码: $code" >> /root/502.log
fi
EOF
chmod +x /root/check_502.sh
# 每分钟跑一次
echo '* * * * * /root/check_502.sh' | crontab -

2. 给 PHP-FPM 开状态页,实时看进程忙闲:

location ~ ^/status$ {
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/tmp/php-cgi-82.sock;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
}
# www.conf 里打开
pm.status_path = /status

然后 curl http://127.0.0.1/status 就能看到 active processes(当前忙的 worker 数)和 max children reached(是否撞过上限),这两个数字是判断要不要扩容的直接依据。

3. 给 WordPress 上缓存,从源头减少 PHP 请求量。对象缓存 + 页面缓存装好之后,同样的访问量下 PHP-FPM 压力能降一大截。

4. 单机扛不住就上负载:Nginx 前面再多加几台 PHP 上游,upstream 里配置多节点,同时用 proxy_next_upstream 让一台挂了自动切另一台:

upstream php_backend {
server 127.0.0.1:9000;
server 127.0.0.1:9001;
}

九、踩坑总结

  1. 502 不等于 Nginx 挂了,先看 error.log 确定上游是谁,别张口就重启 Nginx。
  2. Resource temporarily unavailable = PHP-FPM 满了,这是最高频的 502 原因,重点查 pm.max_children 和 backlog。
  3. max_children 按内存算,别拍脑袋写 100:算错了直接把机器撑爆,反而更容易 OOM 出 502。
  4. 升级 PHP 版本后务必核对 socket 路径,Nginx 和 PHP-FPM 两边不一致是最隐蔽的 502。
  5. 调大超时是兜底不是解药,upstream timed out 的真正解法是让后端变快(查慢 SQL)。
  6. Connection reset by peer 要查 OOM,进程被杀而不是 Nginx 拒绝。
  7. 开 PHP-FPM 慢日志,这是找偶发 502 根因最有效的工具,没有之一。
  8. 加监控和状态页,让 502 在用户反馈之前就被发现,比事后救火重要得多。
  9. 偶发 502 一定要看并发高峰时段,空跑的时候一切正常,压测/晚高峰才现原形。
  10. 配置改完都要 nginx -t 和重载验证,别把配置写错造成更大的故障。

结语:502 看起来吓人,其实是一类「上游连不上或响应无效」的统一表现。把 error.log 的关键词和上面的对照表一匹配,方向基本就定了;剩下就是按进程数 / 路径 / 超时 / OOM 四条线去挖根因。真正花时间的从来不是修好这一次,而是找到「为什么会有这一次」并加监控防住它。

以上就是Nginx 502 Bad Gateway的完整排查指南(PHP-FPM / 上游 / 超时三类原因全覆盖)的详细内容,更多关于Nginx 502 Bad Gateway完整排查的资料请关注脚本之家其它相关文章!

相关文章

  • 详解nginx upstream 配置和作用

    详解nginx upstream 配置和作用

    这篇文章主要介绍了详解nginx upstream 配置和作用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • 配置Nginx服务器展示随机首页与空白图片的方法

    配置Nginx服务器展示随机首页与空白图片的方法

    这篇文章主要介绍了配置Nginx服务器展示随机首页与空白图片的方法,分别用到了ngx_http_random_index_module模块和ngx_http_empty_gif_module模块,需要的朋友可以参考下
    2016-01-01
  • nginx实现动静分离的方法示例

    nginx实现动静分离的方法示例

    Nginx的静态处理能力很强,但是动态处理能力不足,因此,在企业中常用动静分离技术,本文就详细的介绍一下如何使用,感兴趣的可以了解一下
    2021-11-11
  • Debian系统下为PHP程序配置Nginx服务器的基本教程

    Debian系统下为PHP程序配置Nginx服务器的基本教程

    这篇文章主要介绍了Debian系统下为PHP程序配置Nginx服务器的基本教程,这里使用到了FastCGI和php-fpm,需要的朋友可以参考下
    2015-12-12
  • Nginx实现分端口部署两个或多个项目的教程

    Nginx实现分端口部署两个或多个项目的教程

    这篇文章主要为大家详细介绍了Nginx实现分端口部署两个或多个项目的相关教程,其中包含了反向代理配置,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-10-10
  • 详解nginx 中location和 proxy_pass的匹配规则

    详解nginx 中location和 proxy_pass的匹配规则

    location是Nginx中用来匹配客户端请求URI的指令,决定如何处理特定路径的请求,它定义了请求的路由规则,后续的配置(如 proxy_pass)会应用在匹配的请求上,这篇文章主要介绍了nginxlocation和proxy_pass的匹配规则,需要的朋友可以参考下
    2025-04-04
  • Windows 安装 nginx 部署教程

    Windows 安装 nginx 部署教程

    这篇文章主要介绍了Windows安装nginx部署,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • 基于nginx实现上游服务器动态自动上下线无需reload的实现方法

    基于nginx实现上游服务器动态自动上下线无需reload的实现方法

    这篇文章主要介绍了基于nginx实现上游服务器动态自动上下线无需reload,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • SQL操作Pandas DataFrame的三种方式示例详解

    SQL操作Pandas DataFrame的三种方式示例详解

    这篇文章主要为大家介绍了SQL操作Pandas DataFrame的三种方式示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Nginx 499/502/504错误排查的实战指南

    Nginx 499/502/504错误排查的实战指南

    Nginx错误排查与优化ginx错误码详解,499/502/504错误处理方法,包括错误码含义、日志分析、配置优化和问题定位,本文将帮助你解决Nginx日志中的499/502/504问题,需要的朋友可以参考下
    2026-06-06

最新评论