nginx 默认不因非2xx状态码自动重试,仅对显式配置的 error、timeout 及指定 http 状态码(如 http_500/502/503/504)触发 proxy_next_upstream 重试,4xx 和非常规 5xx 需手动添加且重试存在幂等风险,安全做法是由业务层控制重试语义。

Nginx 本身在 upstream 块中不支持对非 2xx 状态码触发自动重试(比如返回 500 或 404 时换另一台后端重发请求),它只对连接失败、超时、500/502/503/504 这类“网关错误”默认重试(取决于 proxy_next_upstream 配置),而不会把 4xx 或自定义 5xx(如 501、506)当作可重试条件,更不会因业务返回的 200 以外状态码(如 401、422、500)主动转发给下一个 upstream server。
proxy_next_upstream 控制哪些错误会触发重试
该指令决定 Nginx 在什么条件下放弃当前后端、尝试下一个。默认值是:
-
error:连接失败、发送请求失败 -
timeout:连接或响应超时 -
http_500、http_502、http_503、http_504:仅这四类 HTTP 状态码
注意:4xx 状态码(包括 400、401、404)默认不重试;501、506、507 等其他 5xx 也不在默认列表中。若需扩展,必须显式添加,例如:
proxy_next_upstream error timeout http_500 http_502 http_503 http_504 http_404 http_501;
无法靠 proxy_next_upstream 实现“非 2xx 全部重试”
Nginx 不支持 http_non_2xx 或类似通配语法。你不能写 http_!2xx 或 http_all_except_2xx。所有要重试的状态码都得逐个列出——但实际中,业务系统可能返回任意非 2xx 状态码(如 422 表单校验失败、429 频率限制、507 存储不足),手动穷举既不可靠也不可持续。
更重要的是:即使你加上 http_422,Nginx 也只会在后端明确返回该状态码且响应体已发出时才触发重试——但此时请求已处理完毕,重试相当于重复执行一次业务逻辑(比如重复扣款、重复发消息),存在严重副作用。
真正安全的做法:由上游应用控制重试逻辑
把重试决策权交给业务层,才是符合语义且安全的方式:
- 后端服务在检测到临时性故障(如依赖 DB 超时、下游服务 503)时,返回明确的可重试状态码(如 503 +
Retry-After),并确保该操作幂等 - Nginx 只对这类有明确语义的失败做重试(如加
http_503) - 对 4xx 或业务性 5xx(如 400 参数错误、500 数据不一致),应直接返回给客户端,禁止重试
如果必须在 Nginx 层统一兜底,可用 mirror 或 Lua(OpenResty)实现条件重放,但需自行保障幂等性和循环保护,复杂度高、维护成本大,一般不推荐。
检查与调试技巧
验证重试是否生效,可通过以下方式:
- 在 upstream 中开启
log_format记录$upstream_addr和$upstream_status,确认是否发生切换 - 用
curl -v观察响应头中的X-Upstream(若自定义)或响应体变化 - 临时让某台后端返回指定状态码(如用
return 503),观察日志中是否出现多次 upstream 地址
务必配合 proxy_next_upstream_tries(默认 0,即无限)和 proxy_next_upstream_timeout(默认 0,不限制总耗时)做合理限制,避免雪崩。











