Nginx不生成业务状态码,核心在于按语义区分可重试性、分组分流、差异化缓存、注入条件响应头及语义修正:503/504可重试,500需谨慎,404不重试;用upstream分组+error_page实现按码分流;proxy_cache_valid按状态码设缓存时长;add_header配合if $status注入Cache-Control;error_page可转码或返回陈旧缓存。

Nginx 本身不生成业务逻辑状态码(如 404、500),它只是透传或转换上游响应的状态码。真正影响用户体验和系统稳定性的,是 Nginx 如何识别、分流、重试、缓存、降级这些状态码——这才是“处理不同业务状态码”的核心策略。
按语义区分可重试性,精准控制 proxy_next_upstream
不是所有 5xx 都该重试,也不是所有错误都适合换节点。关键看状态码背后代表的业务含义:
-
503 / 504:典型临时性故障(过载、超时),适合立即重试 →
proxy_next_upstream error timeout http_503 http_504; -
500:需谨慎 —— 若确认是瞬时资源争用(如 DB 连接池满),且后端多活部署,才加
http_500;否则跳过,避免无效重试放大压力 -
404:确定性结果,重试无意义,除非你有镜像源或 fallback 路径,否则不要加
http_404 - 2xx/3xx:默认不触发重试,也不应重试
用 upstream 分组 + error_page 实现按状态码分流
单一 upstream 无法表达“这个 503 去备用集群,那个 404 去静态兜底”的意图。必须拆分:
- 主集群:
upstream app_primary { server app-a:8080; } - 备用集群(仅承接 5xx):
upstream app_backup_5xx { server app-b:8080 backup; } - 静态兜底(专用于 404):
upstream static_fallback { server static-srv; }
再配合 error_page 触发跳转:
error_page 503 504 = @retry_5xx;
error_page 404 = @fallback_404;
location @retry_5xx {
proxy_pass http://app_backup_5xx;
}
location @fallback_404 {
proxy_pass http://static_fallback;
}为不同状态码设置差异化缓存周期(proxy_cache_valid)
缓存时间必须匹配状态码的业务稳定性:
-
200 206 304:内容稳定 →proxy_cache_valid 200 206 304 1h; -
301:永久重定向 →proxy_cache_valid 301 7d; -
404:防扫描,但不能掩盖新资源 →proxy_cache_valid 404 30s; -
500 502 503 504:临时故障,缓存太长会雪崩 →proxy_cache_valid 500 502 503 504 10s; -
any:兜底未声明的状态码(如 403、429)→proxy_cache_valid any 5s;
⚠️ 注意:这三条前提缺一不可:
-
proxy_cache_path在 http 块中定义缓存区 -
proxy_cache my_cache在 location 中启用 - 上游响应允许缓存(必要时加
proxy_ignore_headers Cache-Control Set-Cookie)
用 add_header + if $status 注入差异化的缓存头(适配 CDN)
expires 指令对 4xx/5xx 无效,必须用条件头:
add_header Expires "" always;
add_header Cache-Control "" always;
if ($status = 200) {
add_header Expires "access plus 1h" always;
add_header Cache-Control "public, max-age=3600" always;
}
if ($status = 404) {
add_header Expires "access plus 2m" always;
add_header Cache-Control "public, max-age=120" always;
}
if ($status ~ ^50[0-4]$) {
add_header Expires "access plus 20s" always;
add_header Cache-Control "public, max-age=20" always;
}同时确保 CDN 开启「缓存错误状态码」,且不返回 private 或 no-store 等冲突头。
把原始错误转化为更准确的状态码或结构化响应
原始 500 可能语义模糊,Nginx 可做语义修正:
- 将上游 500 统一转为 503 并返回 JSON:
error_page 500 =503 @handle_500; location @handle_500 { default_type application/json; return 503 '{"code":"SERVICE_UNAVAILABLE","msg":"Try again later"}'; } - 对 /health 等路径的 503,直接返回陈旧缓存:
error_page 503 = @stale_health; location @stale_health { proxy_cache_use_stale error timeout updating; proxy_pass http://app_primary; }
不复杂,但容易忽略状态码背后的业务含义。真正有效的策略,从来不是统一配置,而是让每个状态码都有明确的处置路径。


















