Nginx 502 排查顺序:从 upstream 连接、超时到 keepalive 配置

举报
yd_217417492 发表于 2026/11/11 11:06:32 2026/11/11
【摘要】 502 不是 Nginx 自己挂了,而是它作为网关拿不到后端有效响应。本文按错误日志、连接、超时、keepalive 和缓冲区五层,给出可以直接照做的排查顺序。

先弄清 502 是谁返回的

502 Bad Gateway 的含义很明确:作为网关的 Nginx 收到了请求,但没有从 upstream(后端)拿到一个合法的响应。它和 504 不同,504 是等后端超时,502 更常见于连不上、连接被提前关闭、响应头不合法这几种情况。

先确认请求到底经过了哪几层。浏览器开发者工具的 Response Headers 里,Server 字段能看出是哪一个 Nginx 返回的;如果前面还有 CDN、WAF 或云负载均衡,它们的错误页样式通常和源站不一样。多台 Nginx 时,先在看得到的那台上查:

nginx -v
nginx -t
tail -n 100 /var/log/nginx/error.log

真正有用的信息几乎都在 error.log 里。把 502 时间点附近的日志抓出来,注意 upstream、connect() failed、prematurely closed、upstream sent too big header 这几个关键词。

第一层:upstream 到底连不连得上

如果日志里是 connect() failed (111: Connection refused),说明 TCP 层就被拒绝了,问题不在 Nginx 配置的转发规则,而在后端没有在那个地址和端口上监听。按顺序确认:

  1. 后端进程是否还活着:systemctl status app、docker ps、ss -lntp | grep 8080;
  2. 监听地址是否正确:只监听 127.0.0.1 时,Nginx 配 proxy_pass http://内网IP:8080 一定连不上;
  3. 端口是否被防火墙或安全组挡住:本机 firewall-cmd --list-all,云主机还要看安全组入方向;
  4. 容器场景下,localhost 在容器里指的是容器自己,不是宿主机,桥接网络要用服务名或宿主机地址。

日志里如果是 no live upstreams,说明 upstream 组里所有节点都被健康检查标记为不可用,先去查健康检查路径和返回码,而不是继续调超时。

还有一类容易误判:connect() failed (110: Connection timed out)。这通常不是后端没起,而是中间网络不通,比如安全组只放行了 ICMP、跨可用区内网 ACL 没放行,或者后端机器负载过高导致 SYN 队列满。

第二层:连接建立了,但响应不完整

upstream prematurely closed connection while reading response header from upstream 表示 TCP 连上了,后端也在处理,但在返回完整响应头之前把连接关了。常见原因:

  • 后端进程崩溃或被 OOM killer 杀掉,可以对照 dmesg -T | grep -i oom;
  • 后端设置了比 Nginx 更短的超时,自己先断开了;
  • 后端返回了非法响应,比如 HTTP/2 明文、响应头里有非法字符;
  • PHP-FPM 的 request_terminate_timeout 到了,子进程被强制结束。

这时不要只盯 Nginx,后端的错误日志往往更直接。用 curl 绕过 Nginx 直连后端,能快速区分是哪一侧的问题:

curl -v -H "Host: example.com" http://127.0.0.1:8080/health
curl -v --resolve example.com:443:127.0.0.1 https://example.com/

第三层:超时参数是不是配反了

Nginx 到 upstream 的超时有三组,含义不同,不要只改一个:

  • proxy_connect_timeout:建立 TCP 连接的时间,默认 60s;
  • proxy_send_timeout:两次成功写操作之间的间隔;
  • proxy_read_timeout:两次成功读操作之间的间隔,默认 60s,长轮询和报表导出最容易被它打断。

如果后端处理时间确实超过 60 秒,先把 proxy_read_timeout 调到业务可接受的值,同时确认上游负载均衡、CDN 的回源超时不小于它,否则只改 Nginx 没有意义。反过来,如果日志显示大量连接在 60 秒整被断开,先怀疑后端卡住,而不是盲目把超时调到 600 秒——那只会把故障拖得更久。

另外注意 proxy_next_upstream 的默认值。多节点时,某个节点返回 502 后 Nginx 可能自动重试下一个节点,用户看到成功,但 error.log 里仍有记录;如果业务不是幂等的,重试还可能造成重复下单,需要显式限制重试条件。

第四层:keepalive 没配对,端口和连接被耗尽

高并发下 502 的常见根因是 Nginx 到 upstream 全是短连接,TIME_WAIT 堆积、临时端口耗尽。只写 keepalive 32; 是不够的,三个配置必须一起出现:

upstream app_backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    keepalive 64;
}

server {
    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://app_backend;
    }
}

proxy_http_version 1.1 是前提,HTTP/1.0 默认短连接;Connection "" 用来清掉客户端带来的 Connection: close;keepalive 控制每个 worker 缓存的空闲长连接数。配好之后可以用 ss -s 看 TIME_WAIT 是否明显下降。

还要确认后端本身支持长连接。Tomcat、Gunicorn、Node.js 一般默认支持,但有些 PHP-FPM 或旧版网关会在响应里主动关连接,这种情况下 Nginx 的 keepalive 池会不断被清空,效果有限。

第五层:响应头或缓冲区超限

日志里出现 upstream sent too big header while reading response header from upstream 时,问题不是后端不可用,而是响应头太大,超过了 Nginx 的缓冲区。典型场景是后端写了很长的 Set-Cookie,或者把调试信息塞进了 Header。可以适当调大:

proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
large_client_header_buffers 4 16k;

但更推荐先找出是哪个响应头在膨胀。用 curl 直连后端看 -D - 输出的头部大小,往往能发现是 Session 里存了太多东西,或者某个中间件重复追加了 Header。把根因修掉,比无限调大缓冲区更稳。

一套可复用的排查顺序

  1. 从 error.log 拿到具体错误关键词,分清 connect failed、prematurely closed、timeout 还是 header too big;
  2. 用 curl 直连后端,确认后端本身是否正常;
  3. 检查监听地址、端口、防火墙和安全组,排除网络层;
  4. 核对三组超时,并同步检查上游负载均衡和 CDN 的回源超时;
  5. 高并发场景检查 upstream keepalive 三件套,观察 TIME_WAIT 和端口占用;
  6. 响应头过大时先定位膨胀的 Header,再考虑调大缓冲区;
  7. 改动后用 nginx -t 校验并平滑 reload,持续观察 error.log 和 5xx 比例。

502 看着吓人,但它其实把原因写在了日志里。按“连接是否建立、响应是否完整、时间是否超限、资源是否耗尽”这条线走,绝大多数问题都能在一次排查内定位,而不是靠反复重启和加大超时碰运气。

小结

Nginx 只是报信的人,不是凶手。先读 error.log 分清错误类型,再用 curl 直连后端做对照,最后才动配置。把超时、keepalive 和缓冲区这三处配好,并给 5xx 加上按 upstream 节点拆分的监控,502 才会从“偶发玄学”变成可定位、可复现的普通故障。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。