Nginx 默认不重试POST等非幂等请求,因已发送请求体后无法确认服务端是否处理,为防重复提交(如下单、发短信)而禁用;需显式启用non_idempotent并配套服务端幂等控制。
直接说结论:Nginx 默认
不会重试 POST、PATCH、LOCK 等非幂等请求
,哪怕配置了
,只要请求已发往上游,就不会转给其他节点——这是为防止重复提交造成业务异常(如重复下单、重复发短信)。但若你确需在特定场景下重试非幂等请求(例如后端节点瞬时崩溃、连接中断),就必须显式启用
,并同步做好服务端幂等防护。
为什么默认不重试非幂等请求
Nginx 的设计逻辑是:一旦请求体(request body)已完整发送到某个 up
stream
server,就认为该请求“已执行”,即使后续收到 500 或超时,也不再转发——因为无法确认服务端是否已处理。这个行为从 1.9.13 版本起固化,默认关闭重试非幂等方法。
常见误区:
以为加了
就能重试所有失败响应,其实对已发出的 POST 无效
未关掉
却观察到重试,大概率是前端重复提交或客户端重试,而非 Nginx 行为
把连接失败(error/timeout)和响应失败(500)混为一谈:前者可能重试,后者对非幂等请求默认不重试
启用 non_idempotent 的正确姿势
仅当明确接受“可能重复执行”的风险,并已在业务层做了强幂等控制(如唯一请求 ID + 数据库唯一索引 / Redis token 校验),才开启该选项。
配置示例:
稿定在线PS
PS软件网页版
下载
关键点说明:
必须与其他条件(如
、
)一起使用,单独写无意义
控制最多尝试几个节点(含首试),设为 2 表示最多轮 2 台;设为 0 表示不限次数(慎用)
是整个重试过程的总耗时上限,不是单次超时
必须配套的服务端幂等措施
开启
后,Nginx 不再替你规避重复,责任完全移交业务层。推荐组合方案:
客户端在请求头带
,服务端入库前校验该 ID 是否已存在
下单类接口要求传入业务唯一键(如用户ID+时间戳+随机串),数据库建联合唯一索引
敏感操作(如支付回调)采用 token 模式:先申请 token,再用 token 提交,token 一次性消费
日志中强制记录原始请求 ID 和最终处理节点 IP,便于故障时追溯是否真发生重复
健康检查与 fail_timeout 的协同作用
即便启用了
,Nginx 仍依赖 u
ps
tream 的被动健康检测来避免持续打挂掉的节点:
:5 秒内失败 2 次,该节点被标记为不可用
被标记后,Nginx 会在
结束后自动试探一次——这次试探也受
影响
所以,如果试探请求是 POST,且你开了
,它也可能被重试到其他节点,形成“健康探测+业务请求”双重重试
建议对健康探测路径(如
)单独配置 location,禁用
,避免干扰。
proxy_next_upstream error timeout http_500non_idempotenthttp_500non_idempotentlocation /api/order {
proxy_pass http://backend;
proxy_next_upstream error timeout http_500 http_502 http_503 non_idempotent;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}non_idempotenterrortimeoutproxy_next_upstream_triesproxy_next_upstream_timeoutnon_idempotentX-Request-ID: uuidnon_idempotentmax_fails=2 fail_timeout=5sfail_timeoutnon_idempotentnon_idempotent/healthnon_idempotent