在排查“POST 为什么会发送两次请求”时,先别急着怪前端或接口“抽风”。多数情况下是浏览器机制、跨域预检、重定向、重试策略、框架开发模式导致的“看起来发了两次”。下面按最常见的真实原因给你一套可直接落地的定位与修复思路。🙂
一、最常见原因总览(先对号入座)🧭
| 现象(你看到的“两次”) | 真正原因 | 是否真的两次 POST | 典型触发条件 |
|---|---|---|---|
一次OPTIONS+ 一次POST | CORS 预检 | 否(只有一次 POST) | 跨域 + 自定义头/JSON/非简单请求 |
POST后立刻又来一次POST/GET | 重定向(301/302/307/308) | 可能是 | URL 末尾斜杠、HTTP→HTTPS、网关跳转 |
| 页面刷新/回退后又提交 | 浏览器表单重复提交 | 是 | 直接<form>提交 + 刷新 |
| 开发环境一点击触发两次 | React StrictMode / 双调用 | 是 | React 18 开发模式副作用检查 |
| 网络抖动下重复到达 | 客户端/代理重试 | 是 | axios/fetch 重试插件、网关重试、LB 重试 |
| 服务端日志显示两次,但前端只发一次 | 网关/边缘层转发两次 | 是 | 多层反向代理、错误重试、超时重投 |
二、第一大类:你以为“POST 两次”,其实是OPTIONS + POST(CORS 预检)✅
典型特征
Network 里看到:
第一次:
OPTIONS /api/xxx第二次:
POST /api/xxx
服务端如果没处理 OPTIONS,可能报 404/405
为什么会预检?
当跨域请求满足以下任一条件,就会触发预检:
Content-Type: application/json自定义 Header(如
Authorization、X-Token)使用
PUT/DELETE等非简单方法
关键点
OPTIONS不是重复 POST,它是浏览器的“放行询问”。真正业务请求仍然只有一次。
三、第二大类:重定向导致看起来发了两次(非常常见)🔁
典型场景 1:URL 末尾斜杠
你请求:
POST /api/login后端路由实际是:
/api/login/服务端返回 301/302/308,让客户端再请求一次
典型场景 2:HTTP → HTTPS
前端请求打到
http://网关强制跳转
https://结果同一次操作发生两次网络交互
关键区别(务实判断)
如果第一次响应码是
301/302/307/308,基本就锁定是重定向链路问题。
说明:
301/302可能把 POST 变 GET(看客户端实现),307/308会保持方法不变,更像“POST 又来一次”。
四、第三大类:开发环境框架“帮你测稳定性”,结果你以为重复提交(React 最典型)🧪
现象
只在开发环境出现
生产环境正常
点击一次,接口打两次
典型根因
React 18 开发环境StrictMode会对某些副作用(如useEffect)进行双调用,用于发现不纯逻辑。
五、第四大类:客户端/网关重试机制(真实的“两次 POST”)📡
常见触发条件
你配置了 axios 重试插件(或业务封装自带重试)
网关对
5xx/超时做了重试负载均衡在上游超时后重投
你会看到什么?
两次请求的参数完全相同
间隔可能是几十毫秒到几秒
第一次可能是
timeout/502/504,第二次成功或再次失败
六、快速定位方法:一套“企业级”排查路径(不绕弯)🧠
1)先看 Network 里的请求方法
如果是
OPTIONS + POST:预检,不是重复提交如果是
POST + POST:继续下一步
2)看第一次响应码
301/302/307/308:重定向链路5xx/timeout:重试链路200也重复:多半是前端触发两次(事件绑定/StrictMode/重复点击)
3)对比两次请求的关键字段
建议你对比:
Request URL是否一致(是否跳转了 https / 加了斜杠)Request Headers里的Origin(是否跨域)Request Payload是否完全一致(是否重试或重复触发)Timing间隔(极短多为前端触发,间隔固定多为重试策略)
七、建议加一招“终极兜底”:幂等设计(让重复请求也伤不到你)🧯
即使你把根因修了,线上仍可能因网络抖动/重试出现重复 POST。企业级做法是:
给 POST 增加 Idempotency-Key(幂等键)
服务端用 Redis/DB 记录已处理的 key
重复请求直接返回第一次结果或拒绝
这相当于给业务上了“保险丝”:允许链路不完美,但结果必须可控。
一张“原因—现象—验证点”速查表(建议收藏)📌
| 原因 | 你看到的现象 | 一眼验证点 |
|---|---|---|
| CORS 预检 | OPTIONS + POST | 第一次方法是 OPTIONS |
| 重定向 | POST 后再来一次 | 第一次响应码 301/302/307/308 |
| StrictMode | 开发环境两次 | 生产环境不复现 |
| 重试 | 间隔后重复 | 第一次 timeout/5xx,或网关有 retry |
| 重复点击/事件绑定 | 手快或绑定两次 | 点击一次触发两次 handler |
如果你愿意把两次请求的 Network 截图(只要方法、状态码、URL、时间线即可)或贴出两条日志的requestId/traceId,我可以直接帮你把原因锁死到某一类,并给出对应的最短修复路径。