服务器502错误怎么解决

结论:502 Bad Gateway 的本质是「网关或代理服务器没能成功从上游拿到响应」——绝大多数情况是连接失败:后端进程没启动、upstream 端口写错、PHP-FPM 进程耗尽、socket 权限不足、SELinux 拦截、DNS 解析失败或后端主动断开。定位顺序是:看 Nginx error.log 里的 upstream 报错原文 → ss -lntup 确认端口是否监听 → curl -I 127.0.0.1:8080 直连后端验证。

502 是站长和运维最常遇到的状态码之一,也是最容易被"重启大法"掩盖的故障。它和 504 只差一个数字,成因却完全不同:502 是"连不上或没响应",504 是"连上了但等太久"。本文只讲 502,把七类成因、对应的日志原文、验证命令和修复动作逐条对应起来,你可以直接拿着日志里的报错原文来对照。

先认清 502:它和 500、503、504 的区别

结论:502 一定出现在有反向代理的链路里,表示"代理这一跳拿不到上游响应"。如果你的架构里没有 Nginx、负载均衡或 CDN,就不可能产生真正的 502——那种情况是应用返回的假 502。

状态码含义谁产生的关键词
500 Internal Server Error应用自身抛异常后端应用(PHP/Java/Python)代码报错
502 Bad Gateway代理连不上上游,或上游返回了非法响应Nginx / LB / CDN连接失败
503 Service Unavailable上游全部被摘除或主动不可用Nginx / LB无健康后端
504 Gateway Timeout代理等上游响应超时Nginx / LB响应超时

一句话记忆:502 = 握手/连接阶段失败,504 = 等待响应阶段超时。Nginx 日志里如果写的是 connect() failed,那是 502;如果写的是 upstream timed out,那是 504。

一分钟定位:先跑这三条命令

结论:不要猜,直接看 Nginx error.log 里的 upstream 报错原文,它能告诉你 80% 的答案。日志是 502 排查的第一现场,命令只有三行。

bash
# 1)实时跟踪 502 相关错误(关键看 upstream: 后面跟的地址与括号里的 errno)
sudo tail -f /var/log/nginx/error.log | grep --line-buffered upstream

# 或者一次性捞最近的记录
sudo grep upstream /var/log/nginx/error.log | tail -20

# 2)确认后端端口到底有没有进程监听
sudo ss -lntup | grep -E ':8080|:9000|php-fpm'

# 3)绕开 Nginx,直接连后端验证(在 Nginx 所在机器上执行)
curl -I -s 127.0.0.1:8080 | head -5
curl -s -o /dev/null -w "%{http_code}\n" 127.0.0.1:8080

第三步的判断逻辑非常关键:

  • curl -I 127.0.0.1:8080 返回 Connection refused → 后端没监听,去启动服务或改端口。
  • 返回正常 200 → 后端是好的,问题在 Nginx 的 upstream 配置(地址/端口/权限/SELinux)。
  • 卡住直到超时 → 后端能握手但不响应,通常转为 504,不属于典型 502。

502 成因对照表:七类问题与日志原文一一对应

结论:拿着 error.log 里的报错原文,对照下表可以直接定位成因。括号里的 errno 是判断的关键。

日志原文(关键片段)errno 含义成因修复动作
connect() failed (111: Connection refused) while connecting to upstream连接被拒,端口无进程后端进程未启动、端口写错、监听 127.0.0.1 但跨机访问启动服务;核对 upstream 端口;改 bind 地址
connect() failed (113: No route to host) while connecting to upstream主机不可达后端主机宕机、安全组/防火墙 DROP检查后端存活与双层防火墙
connect() failed (110: Connection timed out) while connecting to upstream连接阶段超时防火墙丢包、跨地域网络不通查安全组;改用内网地址
no live upstreams while connecting to upstream所有上游都被标记为不可用健康检查连续失败、max_fails 触发排查后端健康;调 fail_timeout
connect() to unix:/run/php-fpm/www.sock failed (13: Permission denied)权限不足socket 属主/权限与 Nginx 运行用户不匹配改 listen.owner/listen.mode
Permission denied 且系统为 RHEL/Rocky/AlmaLinuxSELinux 拦截httpd_can_network_connect 未开启setsebool -P httpd_can_network_connect 1
upstream sent too big header while reading response header响应头超限Cookie/Header 过大,缓冲区不足调大 proxy_buffer_size
upstream prematurely closed connection while reading response header上游提前断连后端进程崩溃、worker 被 OOM 杀查后端日志与 dmesg OOM
recv() failed (104: Connection reset by peer)对端重置连接后端主动断开、协议不匹配核对协议(HTTP/FastCGI)
no resolver defined to resolve xxxDNS 未配置upstream 里写了域名但没配 resolver加 resolver 223.5.5.5 valid=30s;

成因一:后端进程没启动或已经崩溃

结论:这是 502 最常见的原因,占比超过一半。表现是 ss -lntup 里看不到目标端口,curl -I 127.0.0.1:8080 直接 Connection refused。

bash
# 看服务状态与最近日志
sudo systemctl status nginx php-fpm --no-pager
sudo systemctl --failed
sudo journalctl -u php-fpm -n 50 --no-pager

# 启动并设置开机自启(避免重启后再次 502)
sudo systemctl enable --now php-fpm

# 确认端口监听与进程
sudo ss -lntup | grep -E 'php-fpm|:9000|:8080'
ps aux | grep -E 'php-fpm|java|node' | grep -v grep

如果是 PHP-FPM,还要额外确认进程池是否真的有 worker 存活。PHP-FPM 主进程在但所有 worker 都崩掉时,ss 仍能看到 socket 文件存在,但实际无人应答。

成因二:upstream 端口或地址写错

结论:Nginx 里 proxy_pass/fastcgi_pass 写的端口必须与后端实际监听端口一致,差一位就是 502。常见错误包括把 9000 写成 9001、把 8080 写成 80、容器里把 127.0.0.1 当成宿主机。

bash
# 后端实际监听端口
sudo ss -lntup | grep -E 'LISTEN' | grep -E 'nginx|php-fpm|java|node|python'

# 核对 Nginx 配置里写的是哪个端口
sudo nginx -T | grep -E 'proxy_pass|fastcgi_pass|listen'

# 改完务必做语法检查与平滑重载
sudo nginx -t
sudo systemctl reload nginx

容器环境的特殊坑:在 Docker 容器里跑 Nginx 时,proxy_pass http://127.0.0.1:8080 指向的是容器自己的 8080,而不是宿主机的 8080。正确做法是把后端也放进同一个 Docker 网络用容器名访问,或使用 host.docker.internal(Docker 20.10+ 支持)或宿主机的内网 IP。

成因三:PHP-FPM 进程耗尽(pm.max_children)

结论:PHP-FPM 的 pm.max_children 被打满后,新请求排不进进程池,Nginx 侧表现为 502 或长时间无响应。这一类的特征是"低并发正常、一有流量就 502"。

bash
# PHP-FPM 进程池打满时,日志里会出现这条告警
sudo grep -i 'max_children' /var/log/php-fpm/error.log /var/log/php*/php-fpm.log 2>/dev/null | tail -10
# 典型输出:WARNING: [pool www] server reached pm.max_children setting (5), consider raising it

# 查看当前进程数与配置
ps aux | grep -c '[p]hp-fpm'
grep -E '^pm\.' /etc/php-fpm.d/www.conf

计算 pm.max_children 的实用公式:max_children ≈ 可用内存 / 单个 PHP 进程平均内存。例如可用内存 4 GB、单进程约 40 MB,则上限约 100,实际建议留 30% 余量取 70。

ini
; /etc/php-fpm.d/www.conf(RHEL 系)或 /etc/php/8.3/fpm/pool.d/www.conf(Debian 系)
[www]
user = nginx
group = nginx

; 监听方式二选一:TCP 便于跨机,unix socket 本机性能更好
listen = 127.0.0.1:9000
; listen = /run/php-fpm/www.sock

; socket 方式与 Nginx 用户对齐,否则 502 Permission denied
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

pm = dynamic
pm.max_children = 70
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 1000        ; 每个进程处理 1000 个请求后重启,缓解内存泄漏

; 开启状态页便于观测
pm.status_path = /fpm-status

改完重启 PHP-FPM:systemctl restart php-fpm。若仍报 502,继续看成因四和成因五。

成因四:socket 权限不足导致 Permission denied

结论:fastcgi_pass unix:/run/php-fpm/www.sock 报 Permission denied,一定是 socket 文件的属主或权限与 Nginx 运行用户不匹配。这是从 TCP 改成 unix socket 后最常见的 502。

bash
# 看 socket 文件的属主与权限
ls -l /run/php-fpm/www.sock

# 看 Nginx 实际以哪个用户运行
grep -E '^user' /etc/nginx/nginx.conf
ps aux | grep '[n]ginx: worker'

# 临时修复(重启后失效,仅用于验证)
sudo chmod 660 /run/php-fpm/www.sock
sudo chown nginx:nginx /run/php-fpm/www.sock

# 永久修复:在 www.conf 里配 listen.owner / listen.group / listen.mode(见上一节)