Nginx 错误码排查:499/502/504
Nginx 日志里最容易被误判的三个码是 499、502、504:它们看起来都在说“上游有问题”,但责任方完全不同。本章先讲清每个码的判定标准,再给出从 error.log 到上游应用日志的固定排查路径,最后说明哪些配置该调、哪些配置调了也没用。
四个码的责任划分
| 状态码 | 含义 | 责任方 | 常见原因 |
|---|---|---|---|
| 499 | 客户端在 Nginx 返回响应前关闭了连接 | 客户端或上游 | 上游响应太慢用户取消、前端超时比服务端短、用户刷新或关页面、压测客户端提前断开 |
| 502 | 网关从上游收到了无效响应 | Nginx 与上游之间 | 上游进程崩溃、端口没监听、upstream prematurely closed connection、协议不匹配 |
| 504 | 上游在规定时间内没有响应 | 上游应用 | proxy_read_timeout 到期、DB 或三方依赖慢、线程池满、长 GC |
| 500 / 503 | 应用内部错误 / 服务不可用 | 上游应用或 Nginx | 应用抛异常;503 常见于 limit_conn、limit_req 拒绝,或所有上游被标记为不可用 |
499 是 Nginx 侧记录的结果,不代表“一定是客户端的问题”——上游慢到客户端等不下去时,同样记 499,而根因在上游。
排查顺序
1. error.log 关键字 确认错的是哪一类:连接被拒 / 提前关闭 / 超时 / 拒绝
2. access.log 拆维度 $upstream_status、$upstream_response_time、$request_time、$upstream_addr
3. 定位到上游实例 按 upstream_addr 找出是哪台机器哪个端口
4. 上游应用日志 对应时间点的异常、GC、线程池、DB 慢查询
5. Nginx 自身容量 worker_connections、连接数、backlog 是否打满
6. 内核与网络 somaxconn、TIME_WAIT、端口范围、DNS 解析
第一步:在 error.log 里找关键字
tail -f /var/log/nginx/error.log
grep -E 'upstream prematurely closed connection|no live upstreams|upstream timed out|connect\(\) failed|Connection refused' /var/log/nginx/error.log | tail -50
| error.log 关键字 | 对应状态码 | 含义与下一步 |
|---|---|---|
connect() failed (111: Connection refused) | 502 | 上游没监听该端口:查进程是否存活、是否正在重启、端口是否被改 |
upstream prematurely closed connection while reading response header | 502 | 上游进程崩溃或主动断开:查应用日志与 OOM 记录 |
upstream timed out (110: Connection timed out) while reading response header | 504 | 上游处理超时:继续查应用慢在哪、依赖是否也超时 |
no live upstreams while connecting to upstream | 502 / 503 | 后端全部被健康检查判为不可用:先恢复实例,再查健康检查配置 |
upstream sent too big header | 502 | 上游响应头过大:调 proxy_buffer_size 与 proxy_buffers |
worker_connections are not enough | 连接被拒 | 连接数不足:调 worker_connections 与 ulimit -n |
client intended to send too large body | 413 | 请求体超限:调 client_max_body_size |
第二步:access.log 拆维度
前提是日志格式带上了上游字段,推荐下面这种:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time $upstream_status $upstream_addr';
按这个格式,$9 是 $status,$10 是 $request_time,$11 是 $upstream_response_time,$12 是 $upstream_status,$13 是 $upstream_addr。
# 客户端看到的状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 上游返回的状态码分布——这是区分责任方的关键
awk '{print $12}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# 最慢的 20 个请求:request_time、upstream_response_time、status、URL
awk '{print $10, $11, $9, $7}' /var/log/nginx/access.log | sort -rn | head -20
# 只看 499 与 504 的 URL 分布
awk '$9 ~ /^(499|504)$/ {print $9, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 按上游实例聚合 5xx,找出是不是单台机器的问题
awk '$9 ~ /^5/ {print $13}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
| 字段组合 | 判断 |
|---|---|
$status=499,$upstream_response_time 为 - | 请求可能还没到上游,或上游迟迟没返回响应头,客户端已经走了 |
$status=499,$upstream_response_time 很大 | 上游太慢,客户端等不及——责任在上游 |
$status=502,$upstream_status 为 - | 与上游的连接根本没建立成功,查端口与进程 |
$status=502,$upstream_status=502 | 上游自己返回了错误,查应用日志 |
$status=504,$upstream_status=504 | proxy_read_timeout 到期,上游没在预算内响应 |
$request_time 远大于 $upstream_response_time | 慢在 Nginx 侧:限流排队、响应体大、客户端下载慢 |
$request_time 与 $upstream_response_time 接近 | 慢在上游应用 |
第三步:Nginx 自身与内核
nginx -T | grep -E 'worker_processes|worker_connections|keepalive |proxy_(connect|read|send)_timeout|proxy_next_upstream|limit_(conn|req)'
nginx -s reload # 平滑加载新配置,不中断已有连接
ss -s
ss -lnt | grep -E ':80|:443'
grep -i listenoverflows /proc/net/netstat
ulimit -n; cat /proc/sys/net/core/somaxconn; cat /proc/sys/net/ipv4/ip_local_port_range
| 指标 | 判读 |
|---|---|
worker_connections × worker_processes | 可承载的连接上限;做反向代理时,每条客户端连接通常还要占用一条到上游的连接,keepalive 连接池能显著降低消耗 |
ListenOverflows / ListenDrops 大于 0 | accept 队列溢出,需要同时调大 somaxconn 与 listen backlog |
| TIME_WAIT 数量极高 | 到上游未开启 keepalive,端口被快速消耗 |
limit_req 命中率升高 | 不是故障而是保护生效,需要与业务确认拒绝策略 |
处置动作
- 504 居多:先分清是全局变慢还是单实例变慢。单实例 → 摘除并保留现场排查;全局 → 扩容上游或优化慢依赖,同时确认应用侧的依赖超时预算。只放宽 Nginx 超时通常只是把 504 变成更长的等待。
- 502 居多:检查上游进程是否崩溃、是否被 OOMKilled、是否在重启中端口未就绪;可在 Nginx 侧配置
proxy_next_upstream error timeout http_502;与proxy_next_upstream_tries 2做有限重试。 - 499 居多:绝大多数是上游慢导致的用户放弃,先治上游;同时检查前端超时是否比 Nginx 短,避免客户端先超时而服务端还在白干。
- 限流保护:用
limit_conn、limit_req防止单 IP 打满连接与带宽,用proxy_connect_timeout缩短故障节点的等待时间。
预防
- access.log 必须包含
$request_time $upstream_response_time $upstream_status $upstream_addr,缺了字段故障时只能靠猜。 - 对
$upstream_response_time的 P99、5xx 比例、499 比例分别设告警;按$upstream_addr维度做实例级告警,快速发现单实例劣化。 - 上游健康检查加自动摘除,发布时优雅停机:先停止接收新请求,再等待在途请求处理完。
worker_connections、somaxconn、ulimit -n要一起调,只调其中一个通常没效果。
小结:502 是 Nginx 与上游之间的连接或响应问题,504 是上游超时,499 是客户端因上游慢而放弃;排查固定走“error.log 关键字 → access.log 的 upstream 字段 → 上游实例与应用日志 → Nginx 容量与内核”这条路;处置时优先摘除坏实例与优化上游,把超时调大只当作争取时间的临时手段。