必须禁用non_idempotent并仅对error/timeout重试非幂等请求,读接口可有限重试,写接口需nginx层零重试+后端幂等键校验与数据库唯一索引双重防护。

关键不是让 Nginx 去“控制幂等”,而是让它别破坏幂等——故障转移时对非幂等请求(如 POST 创建订单)自动重试,是重复数据的直接推手。必须从 Nginx 配置层切断重试路径,再由后端用幂等键兜底。
禁用 non_idempotent,堵死默认漏洞
Nginx 1.9.13+ 默认不重试 POST/PUT/DELETE,但一旦配置了 proxy_next_upstream ... non_idempotent,或使用旧版本(如 1.8.x),就会开启非幂等方法的隐式重试。这属于高危配置,必须彻底清理:
- 全局搜索所有
upstream和location块,删除所有含non_idempotent的行 - 确认未启用
proxy_next_upstream_tries,或显式设为1(即禁止重试) - 避免写成
proxy_next_upstream error timeout http_500 http_502 non_idempotent这类组合
按 HTTP 方法分策略:读可重试,写零容忍
GET/HEAD 天然幂等,可保留有限重试;但所有写操作接口必须在 Nginx 层做到“一次送达、绝不转发”:
- 对读接口(如
GET /api/orders?uid=123):允许重试,建议加限制:proxy_next_upstream error timeout http_502 http_503;<br>proxy_next_upstream_tries 2;<br>proxy_next_upstream_timeout 6s;
- 对写接口(如
POST /api/order、PUT /api/payment/status):
只保留proxy_next_upstream error timeout;,明确剔除所有http_*状态码(包括 500、503) - 所有写路径统一归入独立 location,便于集中管控:
location ^~ /api/v1/order { proxy_pass http://write_backend; proxy_next_upstream error timeout; }
后端必须独立实现幂等校验
Nginx 不重试只是第一道防线,不能替代业务层防护。客户端需透传幂等标识,服务端强制验证:
- 前端生成标准
Idempotency-Key: <uuid-v4></uuid-v4>,Nginx 透传不修改(确保proxy_pass_request_headers on;) - 后端收到请求后,立即查缓存或数据库是否存在该 key 对应的成功记录;存在则直接返回原响应(HTTP 200 + 原 body)
- 数据库表添加唯一索引,例如:
ALTER TABLE `payment` ADD UNIQUE KEY `uk_idempotency_key` (`idempotency_key`); - 执行插入时用
INSERT IGNORE或INSERT ... ON DUPLICATE KEY UPDATE,避免主键冲突报错
验证是否真生效
改完配置不等于问题消失,必须观测确认:
- 在 Nginx access log 中加入
$upstream_addr $upstream_status,观察同一请求是否命中多个 upstream 地址,或出现类似10.0.1.5:8080 503, 10.0.1.6:8080 200的连发记录 - 后端日志打印
request_id和Idempotency-Key,排查相同 key 是否被多次处理 - 用
curl -X POST模拟请求,同时手动 kill 一个后端实例,验证该 POST 是否始终只到达单个节点











