返回博客

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.defaults.headers.common 是跨实例共享的

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 省心。

正解:创建实例后立即 delete 继承的脏 header
const isolatedApi = axios.create({ baseURL: "..." })
delete isolatedApi.defaults.headers.common["server"]
// 或删除所有你不需要的全局 header

加一行,立竿见影。

排查步骤

遇到"curl 通但浏览器 CORS 失败"时,按这个顺序查:

  1. Chrome Console 报错里,哪个 header 字段触发了 CORS 拒绝?
  2. Network 标签页 → OPTIONS 预检请求 → Access-Control-Request-Headers,和服务端 Access-Control-Allow-Headers 逐项对比,找出多余的那个。
  3. 全局搜索代码里的 axios.defaults.headers.common[,定位污染源。
  4. 确认该 header 是全局设置还是实例设置,选对应修法。

一句话外卖

axios.create() 给的是浅拷贝,全局 header 会悄悄跟进每一个"独立"实例——多后端项目里,始终不要往 axios.defaults.headers.common 写业务专属 header。