axios.create() 实例偷带全局 Header 导致 CORS Preflight 炸掉
项目里有两套后端服务,前端各自用 axios.create() 创建了独立实例——主业务 API 一个,聊天推送服务一个。某天聊天服务突然 CORS 报错,但用 curl 或 Postman 打完全没问题,服务端的 Access-Control-Allow-Origin: * 也配得一清二楚。
折腾了半小时后,在 Chrome Network 的 preflight 请求头里发现了一个多余的 server: 1——它从哪来的?
现象
- Chrome Console 报:
Request header field server is not allowed by Access-Control-Allow-Headers in preflight response - curl 发同样的请求:200,完全正常
- 服务端 CORS 配置没变,其他接口也没问题
- Network 标签页里,OPTIONS 预检请求的
Access-Control-Request-Headers多了一个意料之外的server
根因
axios.create() 内部是对 axios.defaults 做浅拷贝。这意味着 axios.defaults.headers.common 这个对象是所有实例共享的引用,而不是各自独立的副本。
某处初始化主 API 客户端的代码里写了:
// 主 API 模块——用于区分前后端请求的自定义 header
axios.defaults.headers.common["server"] = 1这行代码污染了全局,导致后续用 axios.create() 创建的聊天服务实例,也会在每次请求时带上 server: 1。
curl 发送: Authorization, Content-Type → 服务端 Allow-Headers 覆盖 → 通过
浏览器发送: Authorization, Content-Type, server → "server" 不在 Allow-Headers → 拒绝
curl 不知道 axios 的全局配置,所以不会带上 server;浏览器里跑的 axios 实例却继承了它。
axios.create() 不会给你一个完全隔离的 defaults。headers.common 指向的是同一个对象,任何一处 axios.defaults.headers.common['xxx'] = yyy 都会污染到所有实例。在多后端项目(主 API + 聊天 API + 第三方 API 各用不同实例)中,这个陷阱几乎必踩。
正解
方法 1:在独立实例上删除继承的 header(推荐)
const chatApi = axios.create({
baseURL: "https://chat-service.example.com",
headers: { "Content-Type": "application/json" },
})
// 清除从全局继承的自定义 header
delete chatApi.defaults.headers.common["server"]创建实例后立刻做清理,职责明确,不影响其他实例。
方法 2:污染源改为只在实例上设置,不动全局
// 主 API 实例——不写 axios.defaults,只写 instance.defaults
const mainApi = axios.create({ baseURL: "https://main-api.example.com" })
mainApi.defaults.headers.common["server"] = 1从根上断绝污染。如果能改污染源,这是最干净的方案。
方法 3:用 fetch 替代(彻底隔离)
// fetch 完全不受 axios.defaults 影响
const res = await fetch(url, {
headers: { Authorization: "Bearer " + token },
})如果该实例本就简单,直接换 fetch 省心。
const isolatedApi = axios.create({ baseURL: "..." })
delete isolatedApi.defaults.headers.common["server"]
// 或删除所有你不需要的全局 header加一行,立竿见影。
排查步骤
遇到"curl 通但浏览器 CORS 失败"时,按这个顺序查:
- Chrome Console 报错里,哪个 header 字段触发了 CORS 拒绝?
- Network 标签页 → OPTIONS 预检请求 →
Access-Control-Request-Headers,和服务端Access-Control-Allow-Headers逐项对比,找出多余的那个。 - 全局搜索代码里的
axios.defaults.headers或.common[,定位污染源。 - 确认该 header 是全局设置还是实例设置,选对应修法。
一句话外卖
axios.create()给的是浅拷贝,全局 header 会悄悄跟进每一个"独立"实例——多后端项目里,始终不要往axios.defaults.headers.common写业务专属 header。