跳到主内容
快讯直播
AI智模界
教程

nginx 502 Bad Gateway 排查完全指南

502 是 Nginx 作为反向代理时最常见的错误之一。它不代表 Nginx 本身崩了,而是 Nginx 没能从上游服务拿到合法响应。这篇文章按"从外到内逐层排除"的顺序,把排查路径拆成可执行的动作,每条命令都能直接照抄。

适用场景

适合已经在用 Nginx 做反向代理(代理 Node.js、Python、Java、PHP-FPM、Go 等上游服务),访问页面时看到 502 或间歇性 502 的开发者与运维人员。本文覆盖四类最常见成因:上游服务没起来、超时设置不合理、SELinux 拦截、反向代理配置写错。如果你的 Nginx 直接返回 404 或 403,那是另一类问题,不在本文范围。

环境与前置条件

  • 操作系统:主流 Linux 发行版(CentOS / RHEL / Rocky / AlmaLinux / Ubuntu / Debian 均可),本文命令兼顾 systemd 与 sysvinit
  • Nginx:版本不限,配置语法以官方文档当前版本为准
  • 权限:需要 root 或 sudo,因为要看进程、端口、日志、SELinux 状态
  • 上游服务:任意 HTTP 服务,本文示例用 127.0.0.1:3000
  • 磁盘:/var/log 至少留出几百 MB,日志写满也可能导致异常
  • 无需额外依赖,curlssjournalctltail 都是系统自带或常见工具

分步骤部署

第 1 步:确认错误来自哪一层

先分清是"全部请求 502"还是"部分请求 502",两者排查方向完全不同。

```bash

看错误日志,实时跟踪

tail -f /var/log/nginx/error.log

```

同时用另一个终端反复访问:

```bash

curl -I http://127.0.0.1/

```

典型日志长这样:

```

connect() failed (111: Connection refused) while connecting to upstream

```

```

upstream timed out (110: Connection timed out) while reading response header

```

Connection refused 指向上游没在监听;timed out 指向上游太慢或超时配置太小。日志里会带 upstream: "http://127.0.0.1:3000/..." 字样,直接告诉你 Nginx 在连哪个地址——先记下这个地址,后面每一步都用它。

第 2 步:确认上游服务是否存活

```bash

假设上游是 3000 端口

ss -lntp | grep 3000

```

成功输出应类似:

```

LISTEN 0 511 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=1234,fd=20))

```

如果什么都没有,说明上游根本没监听。这时去查上游服务的状态:

```bash

systemctl status myapp

journalctl -u myapp -n 100 --no-pager

```

如果上游容器化部署:

```bash

docker ps -a | grep myapp

docker logs --tail 100 myapp

```

注意一个容易踩的点:上游监听的是 127.0.0.1:3000,而 Nginx 配置里写的是 localhost:3000。当 /etc/hosts 或解析顺序把 localhost 指向 ::1(IPv6)时,就会连接失败。改成 127.0.0.1:3000 可以排除这个变量。

第 3 步:绕过 Nginx 直接压上游

这一步是分水岭:直接打上游能通,问题就在 Nginx;打不通,问题在上游。

```bash

curl -v http://127.0.0.1:3000/

```

  • 返回 200/正常内容 → 上游健康,继续第 4 步
  • 返回 Connection refused → 上游没监听
  • 长时间卡住 → 上游处理逻辑阻塞(比如同步查询慢、连接池耗尽)
  • 返回 500 → 上游自己的 bug,先修上游

顺手测一下延迟:

```bash

curl -o /dev/null -s -w "connect=%{time_connect} total=%{time_total}\n" http://127.0.0.1:3000/

```

如果 total 经常超过几秒,就该调到第 5 步去改超时。

第 4 步:检查反向代理配置本身

打开站点配置,常见位置:

```bash

ls /etc/nginx/conf.d/

ls /etc/nginx/sites-enabled/ # Debian / Ubuntu

nginx -T | grep -A 20 "proxy_pass"

```

nginx -T 会把所有 include 的配置合并打印出来,比逐个文件翻更快。重点核对四件事:

```nginx

location / {

proxy_pass http://127.0.0.1:3000;

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X-Forwarded-Proto $scheme;

proxy_http_version 1.1;

proxy_set_header Connection "";

}

```

  • proxy_pass 的协议、地址、端口是否和上游实际监听一致
  • 末尾有没有多余的斜杠。proxy_pass http://127.0.0.1:3000/;proxy_pass http://127.0.0.1:3000; 在路径拼接上行为不同,路径错位可能让上游返回 404 而不是 502,但也常被误判
  • 如果用了 upstream 块,检查 server 列表里的地址和权重
  • WebSocket 场景必须显式升级协议,否则连接会被上游拒绝:

```nginx

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

```

改完先测语法,再平滑重载:

```bash

nginx -t

systemctl reload nginx

```

nginx -t 输出 syntax is oktest is successful 才算通过。

第 5 步:调超时参数

默认 proxy_read_timeout 是 60 秒。上游接口偶尔跑十几秒的,可以适当放宽:

```nginx

location / {

proxy_pass http://127.0.0.1:3000;

proxy_connect_timeout 10s;

proxy_send_timeout 60s;

proxy_read_timeout 120s;

send_timeout 120s;

}

```

含义分别是:

  • proxy_connect_timeout:与上游建立 TCP 连接的等待时间
  • proxy_send_timeout:把请求体发给上游的等待时间
  • proxy_read_timeout:两次读操作之间的间隔上限(不是整个请求的总时长,这一点常被误解)
  • send_timeout:向客户端发送响应的等待时间

如果日志里是 upstream timed out,先确认上游真的有慢查询,再调大数值。单纯把超时设成很大只是把问题藏起来,客户端的等待时间会一起变长。同时检查上游侧的连接池、线程数、数据库慢查询,这些才是根因。

第 6 步:检查 SELinux

CentOS / RHEL / Rocky 系默认开启 SELinux,它会阻止 Nginx 向非标准端口或非标准路径发起连接。Ubuntu / Debian 默认不开,可跳过。

```bash

getenforce

```

返回 Enforcing 就说明处于强制模式。查最近的拒绝记录:

```bash

ausearch -m avc -ts recent

dmesg | grep -i avc

```

如果看到 denied { name_connect } 且进程是 nginx,就是 SELinux 拦了。查看 Nginx 允许连接的网络端口:

```bash

semanage port -l | grep http_port_t

```

常见允许的是 80、443、8000、8080 等。如果你的上游跑在 3000、5000、9000,多半不在列表里。两种处理方式:

```bash

方式一:把你的端口加入允许列表(推荐)

semanage port -a -t http_port_t -p tcp 3000

方式二:临时排查用,设为宽容模式确认是不是 SELinux 导致

setenforce 0

```

setenforce 0 只是验证手段,确认原因后应改回 setenforce 1,用方式一或自定义策略解决。如果 semanage 命令不存在:

```bash

RHEL / CentOS / Rocky

dnf install policycoreutils-python-utils

Ubuntu / Debian

apt install policycoreutils-python-utils

```

另外,如果上游是通过 Nginx 之外的路径访问文件(比如 PHP-FPM 的 socket、静态目录),目录的 SELinux 上下文也要对,用 restorecon -Rv /path/to/dir 修复。

第 7 步:确认没有隐式的上游限制

几个不常被注意但会引发 502 的点:

```bash

文件描述符上限,上游和 nginx 都要看

ulimit -n

cat /proc/$(cat /run/nginx.pid)/limits | grep "open files"

```

还有 worker_connectionsworker_rlimit_nofile、上游服务自己的最大连接数。高并发下连接被拒,日志里会出现 worker_connections are not enoughToo many open files

验证部署是否成功

按顺序执行,全部通过说明链路正常:

```bash

1. Nginx 配置语法

nginx -t

2. Nginx 进程在跑

systemctl is-active nginx

3. 上游在监听

ss -lntp | grep 3000

4. 直接打上游

curl -o /dev/null -s -w "%{http_code}\n" http://127.0.0.1:3000/

5. 经过 Nginx

curl -o /dev/null -s -w "%{http_code}\n" http://127.0.0.1/

```

预期结果:第 1 步输出 test is successful;第 2 步输出 active;第 3 步能看到监听行;第 4、5 步都输出 200。然后持续观察错误日志:

```bash

tail -f /var/log/nginx/error.log

```

正常访问时不应再有新的报错行写入。如果第 4 步 200 而第 5 步 502,问题一定在第 4 到第 5 步之间,回到第 4、5、6 步。

常见报错与解决

报错一:connect() failed (111: Connection refused) while connecting to upstream

原因:上游没在指定地址端口监听,或监听在 IPv6 而配置写的是 IPv4(反之亦然),或上游刚崩溃。

解决:

```bash

ss -lntp | grep 3000

systemctl restart myapp

若监听在 ::1,把 proxy_pass 改成 127.0.0.1

```

报错二:upstream timed out (110: Connection timed out) while reading response header

原因:上游处理超过 proxy_read_timeout,常见于慢 SQL、外部 API 调用、冷启动。

解决:

```bash

先定位上游慢在哪

curl -o /dev/null -s -w "total=%{time_total}\n" http://127.0.0.1:3000/slow

再在 nginx 配置中适度调大

proxy_read_timeout 120s; 然后 nginx -t && systemctl reload nginx

```

报错三:connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstream

原因:SELinux 拒绝 Nginx 连接该端口。这个报错和 Connection refused 很像,注意区分关键字 Permission denied

解决:

```bash

ausearch -m avc -ts recent | grep nginx

semanage port -a -t http_port_t -p tcp 3000

systemctl restart nginx

```

报错四:upstream sent invalid header while reading response header

原因:上游返回的响应头不符合 HTTP 规范,比如含非法字符、换行错误,或者上游其实是 HTTPS 而配置里写了 http://

解决:确认协议一致,检查上游框架是否输出了非法头部。

```bash

curl -v http://127.0.0.1:3000/ 2>&1 | head -30

```

报错五:no live upstreams while connecting to upstream

原因:使用了 upstream 块并配置了健康检查,所有后端被标记为不可用。

解决:检查各后端是否真在跑,适当调整 max_failsfail_timeout,避免瞬时抖动把全部节点摘掉。

```nginx

upstream backend {

server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;

server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;

}

```

后续维护

日志轮转:确认 logrotate 对 Nginx 日志生效,避免磁盘写满导致服务异常。

```bash

cat /etc/logrotate.d/nginx

logrotate -d /etc/logrotate.d/nginx # 干跑测试

```

备份配置:改配置前先复制一份带日期的副本。

```bash

cp -a /etc/nginx /etc/nginx.bak.$(date +%F)

```

升级:升级 Nginx 或上游服务前,先在测试环境跑一遍 nginx -t 和一轮回归访问。版本与兼容性以官方文档当前版本为准,不要跨大版本直接上生产。

监控:至少监控三项——Nginx 的 5xx 比例、上游服务存活状态、upstream_response_time。日志格式里加上耗时字段更容易定位:

```nginx

log_format timed '$remote_addr "$request" $status '

'upstream=$upstream_addr rt=$upstream_response_time';

access_log /var/log/nginx/access.log timed;

```

定期自查:把本文第 2 至第 6 步的命令做成一个巡检脚本,每天跑一次,比出故障后再翻日志省事得多。502 的排查核心思路始终是先分层、再定位、后修复,不要在没确认上游状态的情况下反复重启 Nginx。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。