跳到主要内容

技术教程

Nginx 502:先区分上游拒绝连接,还是响应超时

验证与风险信息

适用环境
Docker 24.0.7 / Nginx 1.28.3 / Alpine Linux
验证耗时
约 8 分钟
风险与回滚
只读诊断为低风险;修改配置前备份,nginx -t 通过后平滑重载
最后复核
2026-08-01
验证证据
隔离 Docker 实验首次返回 502,日志记录 connect() failed (111: Connection refused);启动上游后相同请求恢复 200。

Nginx 返回 502 时,最容易造成二次故障的做法是直接重启全部服务。502 只说明 Nginx 没有从上游拿到可用响应;真正需要判断的是:连接根本没有建立,还是连接建立后上游迟迟没有返回响应头。两类故障的证据、影响范围和修复方式都不同。

适用范围与结论

本文适用于 Nginx 作为 HTTP 反向代理、错误日志中包含 while connecting to upstreamwhile reading response header from upstream 的场景。验证环境使用 Docker 24.0.7 与官方 nginx:1.28-alpine 镜像,容器内实际版本为 Nginx 1.28.3。

如果日志出现 connect() failed (111: Connection refused) while connecting to upstream,说明 Nginx 已经尝试连接日志里的上游地址,但目标端口没有进程接受连接。此时优先检查监听地址、端口、进程状态和容器网络,而不是增大 proxy_read_timeout

如果日志出现 upstream timed out ... while reading response header from upstream,通常说明连接已经建立,但上游在 proxy_read_timeout 指定的两次读取间隔内没有传回数据。此时应检查慢请求、线程或连接池耗尽、下游依赖超时和数据库锁等待。

第一步:保存客户端和 Nginx 的只读证据

先从发生故障的同一路径发起请求,记录状态码、响应头和总耗时。不要只看浏览器错误页,因为自定义错误页可能隐藏真实状态。

curl -sv --max-time 10 https://example.com/api/health -o /dev/null

接着读取与请求时间相同的 Nginx 错误日志。systemd 安装和容器安装的常见命令如下:

# systemd
sudo journalctl -u nginx --since "10 minutes ago" --no-pager
sudo tail -n 100 /var/log/nginx/error.log

# Docker
docker logs --since 10m nginx-proxy

从日志中完整记录 requestupstream 和错误阶段。不要只复制“502 Bad Gateway”这一行。上游地址是后续检查必须使用的真实目标;配置文件里看到的服务名可能已经经过变量、DNS 或 upstream 组解析。

第二步:区分拒绝连接与响应超时

连接被拒绝

connect() failed (111: Connection refused) while connecting to upstream,
upstream: "http://127.0.0.1:9000/"

这条日志说明错误发生在建立 TCP 连接阶段。按以下顺序检查:

  1. 在 Nginx 所在的同一网络命名空间确认目标端口是否监听。
  2. 确认应用监听的是 0.0.0.0、容器 IP 还是仅监听 127.0.0.1
  3. 确认 Nginx 使用的端口与应用实际端口一致。
  4. 检查应用进程是否刚刚退出、反复重启或尚未完成启动。
ss -lntp | grep ':9000'
curl -sv --max-time 3 http://127.0.0.1:9000/
systemctl status my-app --no-pager

# Docker 容器状态
docker inspect --format '{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}}' my-app

如果 Nginx 与应用位于不同容器,不要在 Nginx 容器里用 127.0.0.1 指向应用容器;容器内的环回地址只代表当前容器。应使用同一 Docker 网络中的服务名和应用容器监听端口。

读取上游响应超时

upstream timed out (110: Operation timed out) while reading response header from upstream

这里的重点是“reading response header”。应从应用访问日志或 trace 中找到同一请求,比较应用处理耗时、下游调用耗时和 Nginx 的 proxy_read_timeout。官方文档说明,该超时针对连续两次读取之间的空闲时间,并不是整个响应传输的总时长。

grep -R "proxy_read_timeout" /etc/nginx/nginx.conf /etc/nginx/conf.d
curl -sv --max-time 10 http://真实上游地址/health

如果健康检查很快而业务请求超时,问题通常不在网络连通性。继续检查慢查询、连接池、线程池、下游 API 和请求参数,不要直接把超时从 60 秒改到 600 秒。

第三步:执行最小修复

连接拒绝场景的最小修复通常是恢复正确的监听进程或修正错误的上游地址。修改前先运行 nginx -T 保存生效配置,再运行 nginx -t 验证语法。只有语法检查通过后才平滑重载:

sudo nginx -T > /tmp/nginx-effective.conf
sudo nginx -t
sudo systemctl reload nginx

如果只是应用进程退出,应先查看退出原因,再根据既有服务管理流程恢复应用。不要用无限重启掩盖 OOM、配置错误或数据库不可用。

响应超时场景只有在确认应用能够正确完成请求、现有超时确实低于合理业务上限后,才考虑调整 proxy_read_timeout。调整必须同时设置监控和上限,避免大量慢请求长期占用 Nginx 与上游连接。

复核与回滚

修复后重复最初的客户端请求,并确认三件事:状态码恢复为预期值;错误日志不再出现同一 upstream 的失败;应用侧能够找到并正常完成该请求。只看到首页返回 200 不能证明具体 API 已恢复。

配置变更的回滚方式是恢复变更前文件、运行 nginx -t,然后再次平滑重载。服务变更的回滚方式应使用上一版本镜像或原服务管理配置。回滚后同样要重复客户端与日志复核。

V2CE 复现实验记录

隔离实验把 Nginx 的 proxy_pass 指向容器内未监听的 127.0.0.1:9000。第一次请求返回 502,错误日志记录了 connect() failed (111: Connection refused)。随后在同一容器内启动最小 HTTP 响应器,未修改 Nginx 配置;相同请求恢复为 200,响应正文为 backend ok

这组证据证明:在本实验中,502 的直接原因是目标端口无人监听,而不是 Nginx 配置语法、客户端请求或读取超时。完整实验文件和证据保存在 V2CE 的 labs/nginx-502 目录。

参考资料