网站打不开 • • 更新:2026-09-25 • DeepSeek 深度技术推导

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 或应用层主动拦截,请求到达了上游但被拒绝
1020Cloudflare 自定义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 refused
  • systemctl status php-fpm 显示 inactive (dead) 或 failed

涉及的技术栈:

上游类型通信方式常见崩溃原因
PHP-FPMUNIX 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/uWSGIUNIX socket 或 TCP 127.0.0.1:8000worker 超时被杀、模块导入失败
Java/TomcatTCP 127.0.0.1:8080JVM 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

核心机制:

  1. Nginx worker_connections 耗尽:每个 worker 进程能同时处理的连接数有限(默认 512 或 1024)。当并发连接数超过 worker_processes × worker_connections 时,新连接被拒绝。

  2. 上游连接池耗尽:Nginx 的 upstream 块中如果配置了 keepalive,连接池大小有限。当所有保持连接都在使用中,新请求需要等待或失败。

  3. 上游应用的文件描述符耗尽:Linux 默认单进程 ulimit -n 为 1024。当 PHP-FPM 或 Node.js 打开的 socket 数超过此限制,accept() 系统调用失败,新连接被拒绝。

  4. 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 enoughNginx 连接数耗尽增大 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 原因:

  1. 源站 IP 变更但 DNS 未更新:CF 回源到旧 IP,连接超时。
  2. 源站防火墙屏蔽了 CF 的 IP 段:CF 官方 IP 段列表在 cloudflare.com/ips 可查。如果源站只允许特定 IP 访问,需要将 CF 的所有 IP 段加入白名单。
  3. SSL/TLS 模式不匹配:CF 设置为 “Full (Strict)” 但源站使用自签名证书 → CF 拒绝连接 → 502。
  4. 源站返回了 CF 不支持的 HTTP 版本:如源站只支持 HTTP/1.0 而 CF 期望 HTTP/1.1。
  5. 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 协议定义到内核错误码的完整知识链,结合系统化的日志分析,才能在最短时间内定位并修复问题。