$upstream_status是看清后端真实健康状况的唯一可靠入口,它原样捕获每次upstream请求的实际HTTP状态码,包括重试中的全部中间结果,不依赖最终返回客户端的$status。

直接在 log_format 中记录 $upstream_status 是看清后端真实健康状况的唯一可靠入口——它不依赖客户端看到的 $status,而是原样捕获后端返回的每个 HTTP 状态码,包括重试过程中的全部中间结果。
它能暴露被 $status 掩盖的真实问题
$status 只反映 Nginx 最终返回给用户的响应码(比如兜底重试成功后的 200),而 $upstream_status 记录的是每次 upstream 请求的实际结果。常见场景:
- 日志里
$status全是 200,但$upstream_status显示"504,502,200"→ 后端偶发超时或崩溃,靠重试勉强维持可用 -
$upstream_status为"-"或"000"→ Nginx 根本没连上后端(DNS 失败、端口未监听、防火墙拦截) -
$upstream_status长期出现"503"且$upstream_response_time极低 → 后端主动拒绝(如限流、维护模式),不是网络或配置问题
它是精准归因和告警的基础
单看状态码没意义,必须结合上下文才可定位根因。定义 log_format 时带上 $upstream_status,同时绑定以下字段,才能形成可分析的最小信息单元:
-
$upstream_addr:知道是哪台机器出问题(如10.0.3.8:8000),支持实例级下钻 -
$upstream_response_time:区分是连接失败(时间≈0)、处理超时(接近proxy_read_timeout)、还是慢响应(时间高但状态码是 200) -
$time_iso8601或$msec:提供毫秒级时间戳,支撑秒级滑动窗口统计,避免分钟粒度掩盖突发抖动
它支撑结构化日志与自动化分析
原始日志只有记下来,后续工具才有用武之地:
- 用
awk或grep实时过滤50[0-4]|000并触发 Webhook 告警 - 导入 Loki / ELK 后,可通过
logql或grok提取逗号分隔的多个状态,识别重试模式(如"502,502,200") - 配合
map实现条件日志:只对 4xx/5xx 写入独立文件,降低存储开销并聚焦异常
它依赖正确配置才能生效
$upstream_status 不是“开了就一定有”,需确保:
- 后端稳定响应,明确返回标准 HTTP 状态码(而非空响应或非标准码)
- 未启用
proxy_intercept_errors on,否则错误响应被 Nginx 拦截替换,变量值失真 - 未使用
error_page重写 502/503/504,否则 upstream 响应未真正到达,变量为空 - 启用了
proxy_next_upstream(含error timeout http_500...),才能触发重试并填充多值状态


















