一、502 与 504:先分清敌人
当你在浏览器里看到「502 Bad Gateway」,往往意味着 Nginx 作为反向代理,没能从上游(upstream)应用拿到合法响应。本文以生产排错视角,带你从 upstream、FastCGI 到代理缓冲区,一步步定位 502 的真正根因。502 是运维中最常见的“网关类”错误之一,但只要方法对,绝大多数都能在十分钟内闭环。
很多人把 502 和 504 混为一谈,其实二者方向不同。502 Bad Gateway:Nginx 联系上了上游,但上游返回了非法响应(连接被拒、进程崩溃、返回空响应)。504 Gateway Timeout:Nginx 联系上了上游,但上游在超时时间内没有返回。简单记:502 是“上游活着但答非所问或干脆挂了”,504 是“上游太慢答不上来”。下面聚焦更棘手的 502,因为它的根因往往藏在网络与进程的交界处。
二、根因一:upstream 进程没起来或端口错配
最最常见的 502,是反向代理指向的端口根本没有人监听。先绕过 Nginx,直接打上游,能立刻区分是 Nginx 配置问题还是应用本身的问题,这一步可以省掉大量无谓的猜测。
2.1 先确认端口是否有人监听
# 查看 8080 端口是否被监听
ss -lntp | grep 8080
# 或用 curl 直接打 upstream,绕开 Nginx
curl -v http://127.0.0.1:8080/health
如果 curl 直接返回 Connection refused,说明应用没启动,或监听了别的地址/端口(比如只监听了 IPv6 的 ::1,而 Nginx 用 IPv4 的 127.0.0.1 去连)。先拉起应用并确认监听地址,再回到 Nginx 验证。
2.2 核对 upstream 地址与端口
upstream app {
server 127.0.0.1:8080;
}
server {
location / {
proxy_pass http://app;
}
}
一个经典坑:应用监听的是容器内的 127.0.0.1:8080,但 Nginx 在宿主机,跨网络空间访问不到;或者 Docker Compose 里应该用服务名 http://app:8080 而非 127.0.0.1。这类“端口对得上但连不上”的 502,往往藏在容器网络隔离里,用 docker exec 进 Nginx 容器内部 curl 上游是最快验证手段。
2.3 用 upstream 块还是直接 proxy_pass
当只有一个后端时,直接 proxy_pass http://127.0.0.1:8080 与定义 upstream 块等价;但 upstream 块支持负载均衡与健康检查,多节点场景下更易排查“某个节点下线导致 no live upstreams”的问题。无论哪种写法,目标地址必须与实际监听完全一致。
2.4 双栈环境的隐形坑
还有一个容易踩的坑是双栈环境:应用只监听 IPv6 的 ::1,而 Nginx 用 127.0.0.1(IPv4)去连,同样会得到 Connection refused。用 ss -lntp 时务必看清 Local Address 是 0.0.0.0(IPv4 全网卡)还是 :::(IPv6 全网卡),二者并不互通,错配就是 502。
三、根因二:FastCGI / PHP-FPM 场景
对 PHP 站点,502 常常来自 PHP-FPM 挂掉或请求超时。先看 Nginx 的 fastcgi 配置是否指向了正确的 FPM 端口。
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
对应的 PHP-FPM 池配置也别忽视,进程数触顶或脚本死循环都会让人拿到 502:
; www.conf
pm = dynamic
pm.max_children = 50
request_terminate_timeout = 30s
如果 request_terminate_timeout 设成 0(永不超时)但脚本死循环,进程会被占满,新请求只能得到 502。把超时设成合理值,并观察 php-fpm 进程数是否触顶,必要时结合 pm.max_children 扩容。
四、代理超时与 keepalive 连接复用
另一个容易被忽视的 502 来源是代理超时与连接复用配置不当。默认 proxy_read_timeout 是 60 秒,若上游处理偶尔超过该值,Nginx 会主动断开并向上游返回 502/504。
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
upstream app {
server 127.0.0.1:8080;
keepalive 32;
}
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
keepalive 复用后端连接能显著降低新建连接的开销,但要配合 proxy_http_version 1.1 并清空 Connection 头,否则在高并发下连接管理混乱也会引发间歇性 502。
| 超时参数 | 默认值 | 调大场景 |
|---|---|---|
| proxy_connect_timeout | 60s | 上游启动慢、跨机房 |
| proxy_read_timeout | 60s | 报表/导出类慢请求 |
| proxy_send_timeout | 60s | 大请求体上传 |
五、看 error.log:一条日志锁定方向
不要盲猜,直接翻 Nginx 错误日志,502 的“元凶”通常明明白白写在里面:
grep "502" /var/log/nginx/error.log
# 常见三类
# connect() failed (111: Connection refused)
# upstream prematurely closed connection
# upstream timed out
| error.log 信息 | 真实含义 | 排查方向 |
|---|---|---|
| connect() failed (111: Connection refused) | 上游端口没监听 | 检查应用进程 / 端口 |
| upstream prematurely closed connection | 上游进程崩溃或主动断连 | 查应用日志、OOM |
| Permission denied (13) | SELinux / 权限拦截 | 关 SELinux 或设布尔值 |
| no live upstreams | upstream 全部下线 | 健康检查 / 节点状态 |
六、代理缓冲区与请求头过大
某些请求带了大 Cookie 或长请求头,超过 Nginx 默认缓冲区,也会触发 502。适当调大缓冲区,并确保正确透传 Host 与真实 IP。
proxy_buffer_size 16k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
七、SELinux、防火墙与 Docker 网络
如果 error.log 出现 Permission denied (13),大概率是 SELinux 阻止了 Nginx 发起网络连接到上游:
# 临时放行
setsebool -P httpd_can_network_connect 1
# Docker 场景确认网络
docker network ls
docker exec nginx curl -v http://app:8080/health
在 Docker Compose 中,Nginx 与 upstream 必须处于同一自定义网络,且用服务名互访,否则就会得到“连不上”的 502。
八、监控与告警:别等用户报障
502 最好的处理方式是“不发生”或“秒级发现”。在 Nginx 侧可借助 Prometheus 的 blackbox_exporter 对 upstream 做 HTTP 探活,一旦连续探测失败立即告警,而不是等用户投诉。结合站内已有的 Prometheus + Grafana 监控体系,可以把 502 率作为核心 SLO 指标:定义「5xx 比例 = 5xx 状态码数 / 总请求数」,当 502 占比超过阈值(如 0.5%)自动触发告警。这样即使深夜上线引入监听地址错误,也能在用户大规模受影响前被拦截,把故障窗口从小时级压缩到分钟级。
九、真实案例:一次大促前的 502 雪崩
曾有一次上线,压测开始后接口全线 502。error.log 全是 connect() failed (111: Connection refused),但应用进程明明在。最后定位是:新版本把监听地址从 0.0.0.0:8080 改成了 127.0.0.1:8080,而 Nginx 跑在 sidecar 容器里,127.0.0.1 指向的是容器自己,自然连不上。改成 0.0.0.0 后 502 立刻消失。这类“配置一个小改动引发全线 502”的案例,靠的正是“先 curl 直打上游、再看 error.log”的两步法,比盲目重启服务高效得多。
十、生产排错清单
| 步骤 | 动作 | 目的 |
|---|---|---|
| 1 | curl 直打 upstream | 确认应用本身是否正常 |
| 2 | 看 error.log | 锁定连接拒绝 / 超时 / 权限 |
| 3 | 核对 upstream 地址端口 | 排除错配与容器网络 |
| 4 | 查 PHP-FPM / 应用进程数 | 排除进程耗尽 |
| 5 | 检查超时与 keepalive | 排除慢请求与连接复用 |
| 6 | 检查 SELinux / 防火墙 | 排除被拦截 |
| 7 | 加监控告警 | 把故障窗口压到分钟级 |
关于反向代理的基础配置,可参考站内的 Nginx 反向代理实战;若还伴随限流类异常,可对照 Nginx 限流配置。把容器化部署跑顺,可看 Docker 实战。
结语:502 不可怕,可怕的是没有日志就乱改配置。抓住「upstream 是否活着、能否正常响应」这条主线,配合 error.log 的精确报错与一套趁手的监控告警,绝大多数 502 都能在十分钟内定位、在影响扩大前被拦下。




