Nginx 502 排查顺序:从 upstream 连接、超时到 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 配置的转发规则,而在后端没有在那个地址和端口上监听。按顺序确认:
- 后端进程是否还活着:
systemctl status app、docker ps、ss -lntp | grep 8080; - 监听地址是否正确:只监听
127.0.0.1时,Nginx 配proxy_pass http://内网IP:8080一定连不上; - 端口是否被防火墙或安全组挡住:本机
firewall-cmd --list-all,云主机还要看安全组入方向; - 容器场景下,
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。把根因修掉,比无限调大缓冲区更稳。
一套可复用的排查顺序
- 从 error.log 拿到具体错误关键词,分清 connect failed、prematurely closed、timeout 还是 header too big;
- 用 curl 直连后端,确认后端本身是否正常;
- 检查监听地址、端口、防火墙和安全组,排除网络层;
- 核对三组超时,并同步检查上游负载均衡和 CDN 的回源超时;
- 高并发场景检查 upstream keepalive 三件套,观察 TIME_WAIT 和端口占用;
- 响应头过大时先定位膨胀的 Header,再考虑调大缓冲区;
- 改动后用
nginx -t校验并平滑 reload,持续观察 error.log 和 5xx 比例。
502 看着吓人,但它其实把原因写在了日志里。按“连接是否建立、响应是否完整、时间是否超限、资源是否耗尽”这条线走,绝大多数问题都能在一次排查内定位,而不是靠反复重启和加大超时碰运气。
小结
Nginx 只是报信的人,不是凶手。先读 error.log 分清错误类型,再用 curl 直连后端做对照,最后才动配置。把超时、keepalive 和缓冲区这三处配好,并给 5xx 加上按 upstream 节点拆分的监控,502 才会从“偶发玄学”变成可定位、可复现的普通故障。
- 点赞
- 收藏
- 关注作者
评论(0)