HTTP 状态码速查与 502/504 排障:从状态码反推故障在哪一层

一、结论先给

状态码不是"背下来"的,是用来定位故障层的。看到一个 5xx,第一件事是判断它出在链路的哪一跳:

状态码 含义 故障在哪
500 上游代码抛异常 应用本身(看应用日志)
502 Bad Gateway 网关收到了无效响应 上游没起来 / 连接被拒 / 提前断开
503 Service Unavailable 服务不可用 上游主动拒绝、限流、摘除、维护中
504 Gateway Timeout 网关等超时了 上游太慢,超过网关超时时间
499(nginx 私有) 客户端主动断开 用户等不及关了页面,或客户端超时更短

一句话区分:502 是"对方不接/乱说",503 是"对方说它不行",504 是"对方一直不出声"。

👉 想查某个码的完整定义,用 HTTP 状态码速查(在线);不确定是网络还是服务的问题,先用 网络延迟测试 打一发。

二、状态码全表(按排障价值排序)

2xx 成功

码 名称 说明
200 OK 正常响应
201 Created 创建成功,RESTful POST 应返回它 + Location 头
202 Accepted 已接收、异步处理中(下单排队场景)
204 No Content 成功但无响应体,DELETE 常用
206 Partial Content 断点续传/分片下载,Content-Range 配合

3xx 重定向(重点看 301/302/307/308)

码 是否保持请求方法 是否可缓存 用途
301 可能变成 GET 是 永久迁移,SEO 权重转移
302 可能变成 GET 否 临时跳转
303 强制变 GET 否 POST 后跳到结果页
307 保持原方法 否 临时跳转且要保持 POST 体
308 保持原方法 是 永久跳转且要保持 POST 体

最常见的坑:把 POST 接口做了 301/302 跳转,浏览器会把方法降级成 GET 并丢掉请求体,表现为"接口莫名其妙收不到参数"。API 重定向必须用 307/308。

码 说明
304 Not Modified,协商缓存命中,配合 ETag/Last-Modified

4xx 客户端问题

码 名称 排障要点
400 Bad Request 参数格式错、JSON 解析失败
401 Unauthorized 没认证/认证失败,应有 WWW-Authenticate 头
403 Forbidden 认证了但没权限(401 和 403 别混用)
404 Not Found 路径错 / 静态资源没发版
405 Method Not Allowed 路径对但方法不对,响应要带 Allow 头
408 Request Timeout 服务端等请求体超时
409 Conflict 资源冲突(重复创建、版本冲突)
413 Payload Too Large 上传超限,nginx 对应 client_max_body_size
415 Unsupported Media Type Content-Type 不对
422 Unprocessable Entity 语义校验失败(参数对但业务不通过)
429 Too Many Requests 限流,应返回 Retry-After
431 Request Header Fields Too Large Cookie 太大,nginx 调 large_client_header_buffers

5xx 服务端问题

码 名称 说明
500 Internal Server Error 应用未捕获异常
501 Not Implemented 方法未实现
502 Bad Gateway 上游响应无效
503 Service Unavailable 不可用/维护/限流
504 Gateway Timeout 上游超时
505 HTTP Version Not Supported 协议版本不支持

nginx 私有码(只在 nginx 日志里能看到,客户端收不到)

码 含义 典型原因
499 客户端在 nginx 返回前断开 前端超时更短、用户关页面、移动端切后台
444 nginx 主动断连不响应 常配在恶意扫描拦截上
495/496 客户端证书错误/未提供 双向 TLS 场景
497 HTTP 请求发到了 HTTPS 端口 需要 error_page 497 跳转

499 是很重要但常被忽略的信号:如果你的接口 499 占比超过 1%,通常说明"用户实际感受到的响应时间"比你监控的服务端耗时长得多——要么是真的慢,要么是客户端超时设得太激进。

三、排障流程:从域名到应用逐层剥

DNS 解析 → TCP 连接 → TLS 握手 → 请求头/体上传 → 网关转发上游 → 上游处理 → 响应回传

对应工具和现象:

层 检查命令 失败现象
DNS dig +short example.com、nslookup 解析到错 IP 或空
TCP curl -o /dev/null -w '%{time_connect}\n' https://x 连接超时、Connection refused
TLS curl -Iv https://x 2>&1 | grep -i tls、openssl s_client -connect x:443 证书过期、SNI 不匹配
网关 curl -sS -D- -o /dev/null https://x 502/504
上游 应用日志、ss -lntp | grep :8080 上游没监听、连接池满
回传 curl -w '%{time_total}\n' 499、慢

一个通用的耗时分解命令,一次看清时间花在哪:

curl -o /dev/null -sS -w 'DNS:%{time_namelookup}s  连接:%{time_connect}s  TLS:%{time_appconnect}s  首包:%{time_starttransfer}s  总计:%{time_total}s  码:%{http_code}\n' https://example.com/

看哪一段突然变大就知道卡在哪:time_namelookup 大是 DNS,time_connect 大是网络/防火墙,time_appconnect 大是 TLS,time_starttransfer 大是服务端处理慢。

四、502 的定位与处理

502 的本质:nginx 连上游没连上,或连上了但上游没返回合法的 HTTP 响应就断了。

排查顺序:

# 1. 上游还活着吗
ss -lntp | grep 8080
systemctl status your-app

# 2. nginx 能不能直连上游(在 nginx 机器上测,别在自己电脑上测)
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/health

# 3. 看 nginx 错误日志,关键行是 connect() failed
tail -100 /var/log/nginx/error.log | grep -iE "connect|upstream|failed"

典型错误文本与含义:

日志关键字 根因
connect() failed (111: Connection refused) 上游进程没监听
connect() failed (113: No route to host) 防火墙/安全组拦了
upstream prematurely closed connection 上游处理中崩了/被 OOM kill
no live upstreams while connecting to upstream 所有上游都被标记为不可用
upstream sent too big header 上游响应头太大,调 proxy_buffers

OOM 是"偶发 502"的高频原因,查一下:

dmesg -T | grep -i "out of memory" | tail -20
journalctl -k | grep -i oom | tail -20

五、504 的定位与处理

504 是等超时,意味着上游是活的,只是太慢。核心是超时参数链路上每一跳都要比前一跳长,否则前一跳先放弃,就变成 499 或无响应。

nginx 关键超时(放在 location 或 server 里):

location /api/ {
    proxy_pass http://backend;

    proxy_connect_timeout 5s;      # 与上游建连超时,内网建议 3-5s
    proxy_send_timeout    60s;     # 发请求给上游的超时
    proxy_read_timeout    60s;     # 等上游响应的超时,慢接口调到 120s+

    proxy_http_version 1.1;        # 上游 keepalive 必需
    proxy_set_header Connection "";

    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 2;   # 只重试 1 次,避免雪崩
}

PHP-FPM 场景还要多改几个:

location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_read_timeout 120s;
    fastcgi_send_timeout 120s;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
}

同时 PHP 自己也有超时,三者必须对齐(php.ini 的 max_execution_time ≥ request_terminate_timeout ≥ nginx 的 read timeout):

max_execution_time = 120
request_terminate_timeout = 120

超时链必须单调递增:客户端(如 30s)> 网关(60s)> 上游(120s)?不对——正确顺序是外层的要更大,否则外层先断,用户看到的是无响应而不是明确错误。推荐:客户端 60s ≥ nginx 60s ≥ 应用自身 55s,让应用先优雅返回。

六、用日志统计状态码,快速发现异常

不要靠"感觉网站有问题",用数字说话。

# 状态码分布(nginx 默认日志格式,$status 是第 9 个字段)
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

# 最近 5 分钟 5xx 数量
awk -v d="$(date -d '5 min ago' '+%d/%b/%Y:%H:%M:%S')" '$4 > "["d {if ($9>=500) c++} END {print c+0}' /var/log/nginx/access.log

# 5xx 里最慢的 10 个请求(日志加了 $request_time 时,$request_time 是最后一个字段)
awk '$9>=500 {print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -10

# 502 集中在哪些 URL
awk '$9==502 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

建议把 nginx 日志格式加上耗时字段,事后排障会轻松很多:

log_format main_ext '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                    'rt=$request_time urt=$upstream_response_time uaddr=$upstream_addr';
access_log /var/log/nginx/access.log main_ext;

有了 upstream_response_time,一眼就能区分"是 nginx 慢还是上游慢":

# 上游耗时超过 3 秒的请求
awk -F'urt=' '{split($2,a," "); if (a[1]+0 > 3) print}' /var/log/nginx/access.log | head

👉 日志文件往往几千行起步,肉眼翻不动。把关键片段粘进 日志分析工具,它会自动统计级别分布、命中错误规则的行号、异常 Top 和错误高发时段,直接给结论。接口要复现可以用 在线 HTTP 请求工具 发一发。

七、常见误区

  1. 把 502 当 504 修:502 去查进程活着没,504 才去调超时。方向错了会白调半天参数。
  2. 重试次数开太大:proxy_next_upstream_tries 3 在高负载时会把流量放大 3 倍,直接压垮上游。重试 1 次足够。
  3. 只调 nginx 不调应用:nginx 超时调到 300s,应用 30s 就 self-kill,用户照样 502/504。
  4. 忽略 499:499 多说明用户体验已经差了,只是错误没进你的 5xx 监控。
  5. 401/403 混用:没登录返回 401,登录了没权限返回 403。写反了前端没法区分该跳登录页还是提示无权限。
  6. 把业务错误全返回 200:{"code":500,"msg":"失败"} 配 200 状态码,监控和网关全都失效,出问题查不到。

八、几条纪律

  1. 状态码是监控的第一数据源,按码分桶告警,别只看总量 QPS。
  2. 5xx 里单独盯 502/504,各自配不同告警和不同负责人。
  3. 超时链在设计阶段就定死并写进文档:客户端 > 网关 > 上游。
  4. 上线新接口第一件事是确认它有正确的 2xx/4xx 码,而不是一律 200。
  5. 日志必须带 request_time 和 upstream_response_time,否则事后无法复盘。

九、延伸阅读

👉 相关在线工具:HTTP 状态码速查 · 网络延迟测试 · 在线 HTTP 请求 · 日志分析,免登录、纯前端、数据不上传。

还有 65 个免费在线工具

纯前端实现,不用注册,数据不上传服务器。

浏览全部工具