theme: default themeName: "默认主题" title: "Nginx 502 Bad Gateway:我不信邪,实战拆解7种死法"
前言
Nginx跑着跑着返回502,上下游链路长一点的话排查起来颇为费劲——是后端PHP-FPM挂了?还是Nginx本身配置有误?抑或是网络层面的问题?502表面看是网关错误,但根因可能在任何一个环节。
笔者在过去几年里踩过无数次502的坑,总结出7种最常见的"死法",每种都给定位方法和解决方案。遇到502不要慌,按图索骥,一般十分钟内能定位。
502的本质:Nginx没收到后端的响应
在说具体场景之前,先明确一个核心概念:502的本质是Nginx向上游服务器(upstream)发请求,但上游在规定时间内没有返回有效响应。上游可以是PHP-FPM、Node.js服务、Python uWSGI,也可以是另一台反向代理服务器。
理解了这一点,就知道排查思路应该沿着"请求链路"逐层向上追溯。
第一种死法:PHP-FPM进程全挂了
这是最常见也最直接的原因。FPM进程OOM被内核杀掉,或者配置了进程数限制导致请求排队超时。
诊断方法:# 查看PHP-FPM进程状态
ps aux grep php-fpm grep -v grep
查看FPM进程数
pm2 list # 如果用PM2管理 systemctl status php-fpm # Systemd管理
查看FPM错误日志
tail -100 /var/log/php-fpm/error.log
查看OOM Killer是否动过PHP进程
dmesg grep -i "php" tail -20
journalctl --since "1 hour ago" grep -i "php" tail -20
如果看到大量`children pool`或者OOM Killer日志,直接原因就是FPM进程不够用或内存不足。
解决方案:# Nginx侧:适当增加超时时间(临时)
proxy_read_timeout 300; proxy_connect_timeout 75; proxy_send_timeout 300;
PHP-FPM侧:优化进程配置
/etc/php-fpm.d/www.conf
pm = dynamic pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 # 防止内存泄漏累积
第二种死法:Socket文件权限不对
用Unix Socket连接PHP-FPM时,如果Nginx和FPM运行在不同用户,或者Socket文件权限配置错误,Nginx根本无法连接到后端。
诊断方法:# 检查Socket文件是否存在及权限
ls -la /var/run/php-fpm/
查看Socket配置
grep "listen =" /etc/php-fpm.d/www.conf
查看Nginx运行用户
ps aux grep nginx head -3
grep "^user" /etc/nginx/nginx.conf
常见配置问题:
# Nginx侧
location ~ \.php$ { fastcgi_pass unix:/var/run/php-fpm/www.sock; # 路径必须与FPM一致 ... }
; PHP-FPM侧 /etc/php-fpm.d/www.conf
listen = /var/run/php-fpm/www.sock listen.owner = nginx # 关键:Socket文件所有者 listen.group = nginx # 关键:Socket文件所属组 listen.mode = 0660 # 关键:读写权限
Socket权限问题在CentOS 7/8上尤为常见,因为SELinux会额外限制Socket访问。如果常规权限设置后仍不行,试试:
# 临时关闭SELinux排查
setenforce 0
永久放行PHP-FPM的SELinux策略
semanage fcontext -a -t httpd_var_run_t "/var/run/php-fpm(/.*)?" restorecon -Rv /var/run/php-fpm/
第三种死法:FastCGI配置参数对不上
Nginx和PHP-FPM之间通过FastCGI协议通信,涉及一组`fastcgi_*`参数。任何一个路径写错了,都可能导致502。
高频踩坑点:# fastcgi_param SCRIPT_FILENAME 必须准确指向具体的PHP文件
不能写根路径,必须是完整路径
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
错误写法(极易502)
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
正确写法
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
`SCRIPT_FILENAME`写错是最隐蔽的502诱因之一,因为Nginx日志里可能只显示generic 502,看不出具体原因。
快速验证方法:# 用PHP-FPM直接测试某个脚本(绕过Nginx)
php-fpm -tt # 测试语法 php /var/www/html/test.php # 直接执行,看是否有报错
用curl模拟Nginx请求到FastCGI后端
(需要安装fcgiwrap)
第四种死法:上游服务连接数被打满
高并发场景下,PHP-FPM的进程池可能无法承载突发流量,请求堆积后FPM开始拒绝连接,Nginx随之返回502。
诊断方法:# 查看当前等待队列长度
netstat -anp grep :9000 grep ESTABLISHED wc -l
netstat -anp grep :9000 grep WAIT wc -l
查看PHP-FPM状态页面(需要先开启)
在FPM pool配置中加入:
pm.status_path = /status
然后访问:
curl http://127.0.0.1/status?full
输出示例:
pool: www
process manager: dynamic
active processes: 50 ← 这个数字接近max_children说明快打满了
idle processes: 0 ← 全在干活,没有空闲
max children reached: 8473 ← 历史峰值
解决方案:
# Nginx侧:限流 + 重试机制
upstream backend { server 127.0.0.1:9000 weight=5 max_fails=3 fail_timeout=30s; keepalive 100; # 启用长连接减少建连开销 }
同时在前端加限流,防止流量冲击
limit_req_zone $binary_remote_addr zone=api:10m rate=50r/s; limit_req zone=api burst=100 nodelay;
第五种死法:网络层面的诡异问题
有时候502根本不是Nginx或PHP-FPM的锅,而是网络路径上的某台设备在捣乱。最典型的场景是Docker或Kubernetes环境中,容器网络配置不当导致请求无法到达后端。
诊断方法:# 直接telnet到后端端口看是否能连通
telnet 127.0.0.1 9000 curl -v --unix-socket /var/run/php-fpm/www.sock http://localhost/index.php
查看Nginx upstream健康状态
curl -v http://127.0.0.1/upstream_status
在容器环境下检查DNS解析
getent hosts php-fpm-service nslookup php-fpm-service
Docker Compose典型错误:
# docker-compose.yml
services: nginx: depends_on: - php-fpm # 注意:depends_on只是启动顺序,不保证服务就绪 php-fpm: # 正确做法:加上健康检查 healthcheck: test: ["CMD", "php-fpm", "-t"] interval: 10s timeout: 5s retries: 3
第六种死法:后端应用自身崩溃
这是最让人摸不着头脑的情况——PHP-FPM进程还活着,但某些请求让PHP进程直接core dump了。典型的场景包括:调用了不存在的扩展函数、触发了段错误、Zend引擎内部异常等。
诊断方法:# 开启PHP-FPM的slowlog(记录慢请求)
/etc/php-fpm.d/www.conf
slowlog = /var/log/php-fpm/www-slow.log request_slowlog_timeout = 10s
开启PHP错误日志
php_admin_flag[log_errors] = on php_admin_value[error_log] = /var/log/php-fpm/www-error.log
查看PHP-FPM的错误输出
tail -f /var/log/php-fpm/www-error.log
检查是否有core dump
ulimit -c # 查看core dump大小限制
如果是0,临时开启:
ulimit -c unlimited
永久生效需要修改 /etc/security/limits.conf
第七种死法:上游服务响应超长
某些API调用(如调用外部支付接口、大文件上传处理)耗时远超Nginx默认超时限制,Nginx会主动关闭连接。
配置调整:location /api/ {
proxy_pass http://backend; proxy_connect_timeout 60s; proxy_read_timeout 300s; proxy_send_timeout 300s; proxy_buffering off; # 大文件处理时关闭buffer }
502排查黄金四步法
不管遇到哪种502,按这个顺序排查效率最高:
1. 看Nginx错误日志:`tail -f /var/log/nginx/error.log`,每次502都会留下记录 2. 看上游服务状态:PHP-FPM进程是否存活?连接数是否打满? 3. 看响应时间:是在连接阶段就失败,还是连接成功但超时? 4. 做减法隔离:绕过Nginx直接访问后端,逐步缩小范围
# 一行命令直接测PHP-FPM(绕过Nginx)
SCRIPT_NAME=/index.php SCRIPT_FILENAME=/var/www/html/index.php REQUEST_METHOD=GET cgi-fcgi -bind -connect /var/run/php-fpm/www.sock
结语
502是结果,不是原因。看到502不要急着重启Nginx或PHP-FPM——重启能解决问题,但下次还是会遇到。找到根因才是关键。
7种死法覆盖了90%以上的实际场景,剩下的10%往往藏在业务代码里,那就需要业务团队介入了。掌握好这套排查思路,至少能让你的502排查时间从半天缩短到十分钟。
希望本文的教程对你有所帮助。如有疑问或需要专业技术支持,可通过以下方式联系我们:
📞 服务热线:13708730161 💬 微信:eyc1689 📧 邮箱:service@eycit.com
易云城IT服务,您身边的IT专家。