502 Bad Gateway 怎么解决?上游网关崩溃、反向代理与超时排查
遇到 502 Bad Gateway 错误代码?打开呀深度剖析 Nginx/Apache 反向代理作为网关时从上游服务器接收到无效响应的底层机制,提供访客端自检与运维端日志定位完整指南。
502 Bad Gateway 报错怎么解决?上游网关、反向代理与连接排查
Answer Block
502 Bad Gateway 的本质:502 是 HTTP 协议中定义的服务端错误状态码(5xx 类别),表示充当网关或代理角色的服务器(如 Nginx、HAProxy、Cloudflare 边缘节点),在尝试完成请求时,从其上游服务器(upstream)收到了无效响应。注意:502 不是”服务器宕机”的同义词,而是”中间人联系不上或听不懂后端”的信号。排查的核心逻辑是自下而上定位断点:先确认上游进程是否存活(systemctl status php-fpm / ps aux | grep gunicorn),再确认代理配置的 upstream 地址与端口是否正确(nginx -T | grep proxy_pass),然后检查超时参数(proxy_read_timeout、proxy_connect_timeout)与连接队列(listen backlog、worker_connections),最后通过 error.log 中的 connect() failed (111: Connection refused) 或 upstream timed out 等关键字精确定位。对普通访客而言,502 通常是服务端问题而非本地网络问题,强制刷新(Ctrl+F5)或等待几分钟即可,无需修改任何本地设置。
一、502 Bad Gateway 的协议定义:从 RFC 到内核套接字
1.1 RFC 7231 中的精确定义
根据 RFC 7231 §6.6.3(以及此前的 RFC 2616 §10.5.2),502 Bad Gateway 的定义为:
The 502 (Bad Gateway) status code indicates that the server, while acting as a gateway or proxy, received an invalid response from an inbound server it accessed while attempting to fulfill the request.
拆解这句话的每一个关键词:
- “acting as a gateway or proxy”:返回 502 的那台服务器本身不是请求的最终处理者,它只是一个中间人。在典型架构中,这个角色由 Nginx、Apache(mod_proxy)、HAProxy、Envoy、Cloudflare 边缘节点、AWS ALB 等承担。
- “received an invalid response”:注意措辞是”invalid response”而非”no response”。这涵盖了多种情况——上游返回了格式错误的 HTTP 响应、上游在 TCP 握手阶段就拒绝连接(RST)、上游接受了连接但在超时窗口内未返回任何字节、上游返回了不符合 HTTP 规范的响应头。
- “from an inbound server it accessed”:上游服务器可以是同一台机器上的 PHP-FPM 进程(通过 UNIX domain socket 通信)、局域网内的应用服务器(通过 TCP 127.0.0.1:3000 或 10.0.1.5:8080)、甚至是另一个 CDN 节点。
1.2 502 与相邻状态码的边界
| 状态码 | 含义 | 与 502 的关键区别 |
|---|---|---|
| 502 | 网关/代理收到无效响应 | 上游”说了话但听不懂”或”拒绝说话” |
| 503 | 服务不可用 | 服务器本身过载或维护中,通常是主动返回 |
| 504 | 网关超时 | 上游在超时窗口内完全没有响应(502 可能收到了部分无效数据) |
| 403 | 禁止访问 | WAF 或应用层主动拦截,请求到达了上游但被拒绝 |
| 1020 | Cloudflare 自定义 | CF 的 WAF 规则触发,非标准 HTTP 状态码 |
实践中,502 和 504 经常被混用。Nginx 在 proxy_read_timeout 触发时通常返回 504,但如果上游在超时前发送了不完整的响应头然后断开,Nginx 会返回 502。理解这个边界对排查方向至关重要:502 优先查进程存活与配置,504 优先查性能与超时参数。
1.3 从内核视角看 502 的产生
当 Nginx 作为反向代理时,其与上游的通信经历以下阶段:
客户端 → [TCP握手] → Nginx → [TCP握手/UNIX socket连接] → 上游服务
↓
如果 connect() 返回 ECONNREFUSED
(内核返回 RST 包,错误码 111)
↓
Nginx error.log 记录:
"connect() failed (111: Connection refused)
while connecting to upstream"
↓
Nginx 向客户端返回 502
关键的内核级错误码:
- ECONNREFUSED (111):目标端口没有进程监听,内核直接拒绝连接。典型原因:PHP-FPM 未启动、Node.js 进程崩溃。
- ETIMEDOUT (110):TCP 握手超时,目标 IP 不可达或防火墙 DROP 了 SYN 包。
- ECONNRESET (104):上游接受了连接但强制关闭(发送 RST),常见于上游进程崩溃或 OOM Killer 杀死了 worker。
- EPIPE (32):向已关闭的 socket 写入数据,上游在响应前断开。
二、502 产生的四大核心场景
场景 A:上游应用服务崩溃或未启动
这是生产环境中 502 最常见的原因,没有之一。
典型表现:
- Nginx error.log 中出现
connect() failed (111: Connection refused) while connecting to upstream curl http://127.0.0.1:9000返回Connection refusedsystemctl status php-fpm显示inactive (dead)或failed
涉及的技术栈:
| 上游类型 | 通信方式 | 常见崩溃原因 |
|---|---|---|
| PHP-FPM | UNIX socket (/run/php/php8.2-fpm.sock) 或 TCP 127.0.0.1:9000 | 进程池耗尽、配置文件语法错误、OOM |
| Node.js (PM2/systemd) | TCP 127.0.0.1:3000 | 未捕获异常、内存泄漏、依赖缺失 |
| Python Gunicorn/uWSGI | UNIX socket 或 TCP 127.0.0.1:8000 | worker 超时被杀、模块导入失败 |
| Java/Tomcat | TCP 127.0.0.1:8080 | JVM OOM、线程池耗尽 |
UNIX socket 的特殊性:当 PHP-FPM 通过 UNIX socket 通信时,Connection refused 的错误信息会变为 connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)。错误码 2 表示 socket 文件不存在——这通常意味着 PHP-FPM 没有启动,或者启动后 socket 文件被删除(如 /run 被 tmpfs 清理)。
排查命令:
# Linux: 检查 PHP-FPM 状态
systemctl status php-fpm
systemctl status php8.2-fpm
# 检查端口监听
ss -tlnp | grep :9000
ss -tlnp | grep :3000
# 检查 UNIX socket 文件是否存在
ls -la /run/php/php8.2-fpm.sock
# 检查进程
ps aux | grep -E "php-fpm|node|gunicorn|uwsgi"
# macOS: 检查端口监听
lsof -iTCP:9000 -sTCP:LISTEN
lsof -iTCP:3000 -sTCP:LISTEN
# Windows PowerShell: 检查端口监听
Get-NetTCPConnection -LocalPort 9000 -State Listen
netstat -ano | findstr :9000
场景 B:反向代理配置错误
典型表现:
- Nginx error.log 中出现
connect() failed (110: Connection timed out)或no live upstreams while connecting to upstream - 502 是持续性的、100% 复现的,而非间歇性的
- 直接 curl 上游地址可以正常响应,但通过 Nginx 访问就是 502
常见配置错误:
# 错误示例 1:proxy_pass 指向了错误的端口
location / {
proxy_pass http://127.0.0.1:8080; # 实际应用监听在 3000
}
# 错误示例 2:upstream 块中的 server 地址拼写错误
upstream backend {
server 127.0.0.1:9000; # 实际是 127.0.0.1:9001
}
# 错误示例 3:UNIX socket 路径不匹配
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock; # 实际安装的是 php8.2
}
# 错误示例 4:proxy_pass 末尾斜杠导致路径拼接错误
location /api/ {
proxy_pass http://backend/v1/; # 请求 /api/users 变成 /v1/users
}
排查命令:
# 导出 Nginx 完整配置并搜索 proxy_pass
nginx -T | grep -n "proxy_pass\|fastcgi_pass\|upstream"
# 测试配置文件语法
nginx -t
# 直接测试上游连通性
curl -v http://127.0.0.1:9000/
curl -v --unix-socket /run/php/php8.2-fpm.sock http://localhost/status
# 检查 Nginx 解析的 upstream 地址
nginx -T | grep -A5 "upstream"
# Windows: 测试端口连通性
Test-NetConnection -ComputerName 127.0.0.1 -Port 9000
场景 C:上游响应时间过长触发 proxy_read_timeout
典型表现:
- 502/504 是间歇性的,通常发生在特定接口(如报表导出、大数据查询)
- Nginx error.log 中出现
upstream timed out (110: Connection timed out) while reading response header from upstream - 上游应用日志显示请求仍在处理中,但 Nginx 已经断开了连接
Nginx 超时参数详解:
location / {
proxy_pass http://backend;
# 与上游建立 TCP 连接的超时时间(默认 60s)
proxy_connect_timeout 60s;
# 向上游发送请求的超时时间(默认 60s)
proxy_send_timeout 60s;
# 等待上游响应的超时时间(默认 60s)—— 最常触发 502/504 的参数
proxy_read_timeout 60s;
}
当 proxy_read_timeout 触发时,Nginx 会关闭与上游的连接,并向客户端返回 504 Gateway Timeout。但如果上游在超时前发送了部分响应头然后断开,Nginx 会返回 502。
FastCGI 的超时参数(PHP-FPM 场景):
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_read_timeout 300s; # 默认 60s
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
}
同时需要检查 PHP-FPM 自身的 request_terminate_timeout:
; /etc/php/8.2/fpm/pool.d/www.conf
request_terminate_timeout = 300s
; 如果 PHP 脚本执行超过这个时间,FPM 会杀死 worker
; 此时 Nginx 收到的是上游断开连接,返回 502
排查命令:
# 查看 Nginx error.log 中的超时记录
tail -f /var/log/nginx/error.log | grep -i "timed out"
# 查看 PHP-FPM 慢日志
tail -f /var/log/php8.2-fpm.log | grep -i "execution timed out"
# 实时监控上游响应时间
curl -o /dev/null -s -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" http://your-domain.com/slow-endpoint
场景 D:连接池耗尽或并发流量打爆服务器
典型表现:
- 502 在高并发时段集中出现,低峰期正常
- Nginx error.log 中出现
connect() failed (99: Cannot assign requested address)或worker_connections are not enough - 上游应用日志显示
Too many connections或EMFILE: too many open files
核心机制:
-
Nginx worker_connections 耗尽:每个 worker 进程能同时处理的连接数有限(默认 512 或 1024)。当并发连接数超过
worker_processes × worker_connections时,新连接被拒绝。 -
上游连接池耗尽:Nginx 的
upstream块中如果配置了keepalive,连接池大小有限。当所有保持连接都在使用中,新请求需要等待或失败。 -
上游应用的文件描述符耗尽:Linux 默认单进程
ulimit -n为 1024。当 PHP-FPM 或 Node.js 打开的 socket 数超过此限制,accept()系统调用失败,新连接被拒绝。 -
TCP backlog 溢出:当 SYN 队列或 accept 队列满时,内核会丢弃新的 SYN 包,客户端表现为连接超时。
排查命令:
# 检查 Nginx 当前连接数
nginx -T | grep worker_connections
ss -s # 查看系统 socket 统计
# 检查文件描述符限制
ulimit -n
cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep "open files"
# 检查 PHP-FPM 进程池状态
# 需要在 pool.d/www.conf 中启用 status page
curl http://127.0.0.1/status?full
# 检查系统级连接跟踪表
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 检查 TIME_WAIT 连接数
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
修复方向:
# nginx.conf 全局调优
worker_processes auto;
events {
worker_connections 4096;
multi_accept on;
}
# upstream 启用 keepalive 连接池
upstream backend {
server 127.0.0.1:9000;
keepalive 32; # 保持 32 个空闲长连接
}
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 清除 Connection: close
}
; PHP-FPM 进程池调优
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500 ; 每个 worker 处理 500 请求后重启,防止内存泄漏
三、普通访客如何自检
作为普通用户,你无法直接访问服务器日志,但可以通过以下步骤快速判断问题范围:
3.1 强制刷新绕过代理缓存
按 Ctrl+F5(Windows/Linux)或 Cmd+Shift+R(macOS)强制刷新,这会:
- 跳过浏览器本地缓存
- 发送
Cache-Control: no-cache和Pragma: no-cache请求头 - 部分 CDN 会因此回源到最新内容
如果强制刷新后正常,说明是缓存层的问题(CDN 缓存了 502 响应),而非源站持续故障。
3.2 使用第三方工具验证是否为全站宕机
- downforeveryoneorjustme.com:输入域名,从第三方节点发起请求,判断是全球性宕机还是你的本地网络问题。
- isitdownrightnow.com:类似功能,提供响应时间历史。
- 多个地理位置测试:使用
curl -I https://domain.com从不同网络(如手机热点)测试。
3.3 浏览器开发者工具分析
按 F12 打开 DevTools → Network 标签 → 刷新页面:
- 查看 502 响应的 Response Headers 中是否有
Server: nginx或Server: cloudflare,判断是哪一层返回的 502。 - 如果响应头中有
CF-RAY,说明是 Cloudflare 返回的 502,问题在 CF 到源站之间。 - 查看 Timing 标签,如果 TTFB(Time To First Byte)很短就返回了 502,说明是连接被拒绝(场景 A/B);如果 TTFB 接近超时时间才返回 502,说明是超时(场景 C)。
3.4 访客能做什么、不能做什么
| 能做 | 不能做 |
|---|---|
| 强制刷新、清除缓存 | 修复服务器配置 |
| 等待 5-10 分钟后重试 | 重启上游服务 |
| 切换网络(4G/WiFi)排除本地问题 | 修改 Nginx 超时参数 |
| 通过第三方工具确认是否全站故障 | 联系 CDN 提供商 |
| 向网站管理员反馈具体时间和 URL | 自行修复 |
四、运维/站长经典排查日志定位与修复命令
4.1 第一步:定位 502 的来源层
# 查看 Nginx error.log 最新 50 行
tail -50 /var/log/nginx/error.log
# 实时监控 error.log
tail -f /var/log/nginx/error.log
# 搜索特定时间段的 502 相关错误
grep "502\|Bad Gateway\|upstream" /var/log/nginx/error.log | tail -100
# 统计各类上游错误出现频率
grep -oP "connect\(\) failed \(\d+:" /var/log/nginx/error.log | sort | uniq -c | sort -rn
4.2 第二步:根据错误关键字定位根因
| error.log 关键字 | 根因 | 修复方向 |
|---|---|---|
connect() failed (111: Connection refused) | 上游进程未启动 | 启动上游服务 |
connect() failed (2: No such file or directory) | UNIX socket 文件不存在 | 检查 FPM 配置和启动状态 |
connect() failed (110: Connection timed out) | 上游 IP 不可达或防火墙拦截 | 检查网络和 iptables |
upstream timed out while reading response header | 上游处理超时 | 增大 proxy_read_timeout 或优化应用 |
no live upstreams | 所有 upstream 节点都被标记为 down | 检查健康检查配置 |
worker_connections are not enough | Nginx 连接数耗尽 | 增大 worker_connections |
Too many open files | 文件描述符耗尽 | 增大 ulimit -n 和 worker_rlimit_nofile |
4.3 第三步:验证上游服务状态
# 检查所有相关服务状态
systemctl status nginx php8.2-fpm mysql redis
# 检查端口监听情况
ss -tlnp | grep -E "80|443|9000|3000|8000|8080"
# 直接测试上游响应
curl -v http://127.0.0.1:9000/status
curl -v --unix-socket /run/php/php8.2-fpm.sock http://localhost/ping
# 检查进程是否存在
ps aux | grep -E "php-fpm|node|gunicorn" | grep -v grep
# 检查最近的 OOM Killer 记录
dmesg | grep -i "oom\|killed process" | tail -20
journalctl -k | grep -i "oom" | tail -20
4.4 第四步:修复与验证
# 重启上游服务
systemctl restart php8.2-fpm
# 或
pm2 restart all
# 重载 Nginx 配置(不中断现有连接)
nginx -t && nginx -s reload
# 验证修复
curl -I https://your-domain.com
# 期望看到 HTTP/2 200
# 持续监控 502 是否复现
watch -n 5 'curl -o /dev/null -s -w "%{http_code}\n" https://your-domain.com'
4.5 跨平台诊断命令速查
Windows CMD:
netstat -ano | findstr :9000
tasklist | findstr nginx
curl -I http://127.0.0.1:9000
Windows PowerShell:
Get-NetTCPConnection -LocalPort 9000 -State Listen
Get-Process -Name nginx
Invoke-WebRequest -Uri http://127.0.0.1:9000 -Method HEAD
Test-NetConnection -ComputerName 127.0.0.1 -Port 9000
macOS Terminal:
lsof -iTCP:9000 -sTCP:LISTEN
lsof -iTCP:80 -sTCP:LISTEN
curl -v http://127.0.0.1:9000
sudo dtruss -p $(pgrep nginx | head -1) # 类似 strace
Linux:
ss -tlnp | grep :9000
strace -p $(pgrep -f "nginx: worker" | head -1) -e trace=network
tcpdump -i lo port 9000 -w /tmp/upstream.pcap
五、五个高价值长尾 FAQ
H3:Nginx 返回 502 但上游服务明明在运行,curl 直接访问也正常,为什么?
这是最令人困惑的场景之一。可能的原因有以下几个层次:
第一层:Nginx 与你的 shell 使用了不同的网络命名空间。 如果上游运行在 Docker 容器中,容器有自己的 network namespace。你在宿主机上 curl 127.0.0.1:9000 可能访问的是宿主机上的另一个进程,而 Nginx 配置的 proxy_pass 指向的是容器 IP(如 172.17.0.2:9000)。检查方法:docker inspect <container> | grep IPAddress,确认 Nginx 配置中的 IP 与容器实际 IP 一致。更常见的做法是使用 Docker 自定义网络,通过容器名解析。
第二层:SELinux 或 AppArmor 阻止了 Nginx 的网络连接。 在 CentOS/RHEL 上,SELinux 默认策略可能禁止 Nginx 连接到非标准端口。检查方法:ausearch -m avc -ts recent | grep nginx 或 dmesg | grep avc | grep nginx。修复:setsebool -P httpd_can_network_connect 1。
第三层:Nginx 的 upstream 块中配置了多个 server,其中部分不可用。 如果配置了 server 127.0.0.1:9000; server 127.0.0.1:9001; 而 9001 没有服务,Nginx 默认会将请求轮询到两个 server。当轮询到 9001 时返回 502。检查方法:nginx -T | grep -A10 upstream。修复:移除不可用的 server,或配置 max_fails 和 fail_timeout 让 Nginx 自动剔除故障节点。
第四层:IPv6 vs IPv4 解析差异。 如果 proxy_pass http://localhost:9000,Nginx 可能解析 localhost 为 ::1(IPv6),而你的上游只监听了 127.0.0.1(IPv4)。检查方法:nginx -T | grep proxy_pass,将 localhost 改为 127.0.0.1 显式指定 IPv4。
第五层:Nginx 的 proxy_pass 使用了变量,导致 DNS 解析行为不同。 当 proxy_pass 中包含变量时(如 proxy_pass http://$backend;),Nginx 会在运行时解析域名,且不会使用 upstream 块。此时如果 DNS 解析失败或解析到错误 IP,就会 502。检查方法:nginx -T | grep proxy_pass 确认是否使用了变量。
H3:502 和 504 到底有什么区别?为什么有时候刷新一下 502 就变成 504 了?
502 和 504 的区别在于上游响应的性质,而非时间长短。
502 Bad Gateway:Nginx 与上游建立了 TCP 连接(或尝试建立),但收到了无效响应。具体包括:
- 连接被拒绝(RST)→ 上游进程不存在
- 连接建立后上游立即关闭(FIN/RST)→ 上游崩溃
- 上游返回了格式错误的 HTTP 响应 → 上游协议实现有 bug
- 上游发送了部分响应头后断开 → 上游处理中途崩溃
504 Gateway Timeout:Nginx 与上游建立了连接,发送了请求,但在 proxy_read_timeout 内没有收到完整的响应头。上游可能仍在处理请求,只是太慢了。
为什么刷新后 502 变 504? 这通常发生在以下场景:上游服务正在启动过程中。第一次请求时,上游进程还没有开始监听端口,Nginx 收到 Connection refused → 502。几秒后你刷新,上游进程已经启动并接受了连接,但还在初始化(如加载配置、连接数据库),无法在超时时间内响应 → 504。再过几秒刷新,上游完全就绪 → 200。
另一种可能是:上游有多个 worker 进程,部分 worker 崩溃部分正常。Nginx 轮询到崩溃的 worker → 502;轮询到正常但繁忙的 worker → 504。这种情况下需要检查上游的进程管理配置(如 PHP-FPM 的 pm.max_children 是否过小导致频繁重启)。
从排查角度:502 优先查”进程是否活着”和”配置是否正确”,504 优先查”性能是否足够”和”超时是否合理”。
H3:Cloudflare 返回 502 和源站 Nginx 返回 502 有什么区别?如何判断是哪一层的问题?
判断方法非常直接:看响应头。
curl -I https://your-domain.com
- 如果响应头中有
Server: cloudflare且包含CF-RAY: xxxxx,说明 502 是 Cloudflare 边缘节点返回的。此时问题可能出在:CF 到源站的连接失败(源站 IP 变了、防火墙封了 CF 的 IP 段)、源站返回了 CF 无法解析的响应、CF 的 SSL 模式配置错误(如 Full Strict 模式下源站证书过期)。 - 如果响应头中有
Server: nginx且没有CF-RAY,说明请求已经到达源站 Nginx,502 是源站产生的。此时按照本文第四节的排查流程处理。
Cloudflare 特有的 502 原因:
- 源站 IP 变更但 DNS 未更新:CF 回源到旧 IP,连接超时。
- 源站防火墙屏蔽了 CF 的 IP 段:CF 官方 IP 段列表在 cloudflare.com/ips 可查。如果源站只允许特定 IP 访问,需要将 CF 的所有 IP 段加入白名单。
- SSL/TLS 模式不匹配:CF 设置为 “Full (Strict)” 但源站使用自签名证书 → CF 拒绝连接 → 502。
- 源站返回了 CF 不支持的 HTTP 版本:如源站只支持 HTTP/1.0 而 CF 期望 HTTP/1.1。
- CF 的 1020 错误:这不是 502,而是 CF 的 WAF 规则触发,返回自定义状态码 1020。响应体中会包含 “Access denied” 和规则 ID。
排查步骤:
# 绕过 Cloudflare 直接访问源站
curl -I --resolve your-domain.com:443:源站IP https://your-domain.com
# 如果直接访问源站正常,说明问题在 CF 层
# 如果直接访问源站也 502,说明问题在源站
H3:PHP-FPM 的 UNIX socket 和 TCP 端口两种通信方式,哪种更容易导致 502?如何选择?
两种方式各有优劣,502 的触发条件也不同。
UNIX socket 方式:
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
- 优势:不经过 TCP/IP 协议栈,延迟更低(约减少 0.1-0.3ms),不占用端口号,不受
net.ipv4.ip_local_port_range限制。 - 502 风险:socket 文件不存在(FPM 未启动或
/run被清理)、socket 文件权限错误(Nginx worker 用户无权限读写)、socket backlog 溢出(listen.backlog默认 511,高并发时容易满)。 - 典型错误:
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)或(13: Permission denied)。
TCP 方式:
fastcgi_pass 127.0.0.1:9000;
- 优势:可以跨主机通信(FPM 和 Nginx 在不同服务器)、可以使用
ss和tcpdump等工具直接观测、不受文件权限限制。 - 502 风险:端口未监听、防火墙拦截、TIME_WAIT 连接过多导致端口耗尽、TCP backlog 溢出。
- 典型错误:
connect() failed (111: Connection refused)或(99: Cannot assign requested address)。
选择建议:
- 单机部署优先用 UNIX socket,性能略优。
- 需要跨机部署或用 Docker 分离容器时用 TCP。
- 如果使用 UNIX socket,确保
listen.owner和listen.group与 Nginx worker 用户一致(通常都是www-data或nginx)。 - 如果使用 TCP,确保
listen = 127.0.0.1:9000而非listen = 0.0.0.0:9000(后者暴露到公网有安全风险)。
UNIX socket 的 backlog 调优:
; php-fpm pool 配置
listen = /run/php/php8.2-fpm.sock
listen.backlog = 4096 ; 默认 511,高并发时增大
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
H3:如何配置 Nginx 在返回 502 时自动重试或降级,减少对用户的影响?
Nginx 提供了多层机制来处理上游故障:
第一层:upstream 健康检查与故障转移
upstream backend {
server 127.0.0.1:9000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:9001 max_fails=3 fail_timeout=30s backup;
keepalive 32;
}
max_fails=3:连续失败 3 次后标记为不可用。fail_timeout=30s:标记不可用后 30 秒内不再尝试,30 秒后重新探测。backup:备用节点,仅当主节点全部不可用时才使用。
第二层:proxy_next_upstream 自动重试
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
当上游返回 502 或超时时,Nginx 自动将请求转发到下一个 upstream 节点。注意:非幂等请求(POST/PUT/DELETE)默认不重试,因为可能已经在原上游执行了副作用。如果确认应用支持幂等,可以显式配置 proxy_next_upstream 包含 non_idempotent。
第三层:自定义错误页面降级
proxy_intercept_errors on;
error_page 502 503 504 /custom_50x.html;
location = /custom_50x.html {
root /var/www/error-pages;
internal;
}
这样用户看到的是友好的错误页面而非 Nginx 默认的 502 页面。但注意:这不能解决 502 本身,只是改善用户体验。
第四层:应用层降级(需要开发配合)
在应用代码中实现熔断器模式(Circuit Breaker),当检测到下游依赖(如数据库、缓存)不可用时,返回缓存数据或默认值,而非直接崩溃导致 502。
第五层:CDN 层的 stale-while-revalidate
如果使用了 CDN,配置 stale-while-revalidate 和 stale-if-error 指令,当源站返回 502 时,CDN 可以继续提供缓存的旧版本内容:
Cache-Control: max-age=3600, stale-while-revalidate=86400, stale-if-error=86400
这意味着即使源站宕机,用户在 24 小时内仍能看到缓存内容,为运维争取修复时间。
验证配置是否生效:
# 模拟上游故障(临时停止 PHP-FPM)
systemctl stop php8.2-fpm
# 测试 Nginx 是否自动重试到 backup 节点
curl -I https://your-domain.com
# 查看 error.log 确认重试行为
tail -f /var/log/nginx/error.log | grep "upstream"
总结:502 排查的决策树
用户报告 502
│
├─ 强制刷新后正常?→ CDN/浏览器缓存问题,无需处理
│
├─ downforeveryoneorjustme 显示全站宕机?
│ │
│ ├─ 是 → 服务端问题,继续排查
│ └─ 否 → 本地网络问题,切换网络重试
│
└─ 服务端排查
│
├─ 查看 Nginx error.log
│ │
│ ├─ "Connection refused" → 上游进程未启动 → systemctl start
│ ├─ "No such file or directory" → socket 文件缺失 → 检查 FPM 配置
│ ├─ "Connection timed out" → 网络/防火墙问题 → 检查 iptables
│ ├─ "upstream timed out" → 超时 → 增大 proxy_read_timeout
│ └─ "no live upstreams" → 所有节点故障 → 检查 upstream 配置
│
├─ 检查上游进程状态 → ps aux / systemctl status
│
├─ 检查端口监听 → ss -tlnp
│
├─ 检查 Nginx 配置 → nginx -T | grep proxy_pass
│
└─ 检查系统资源 → ulimit -n / dmesg | grep oom
502 从来不是一个孤立的问题,它是整个请求链路中某个环节断裂的信号。掌握从 HTTP 协议定义到内核错误码的完整知识链,结合系统化的日志分析,才能在最短时间内定位并修复问题。