fetch 调用,在 content script 里和在 background 里表现并不相同。
Extension.js 不改变这些规则中的任何一条。它确实会给自己的开发服务器设置响应头,也确实会在开发期给 CSP 打补丁。这两件事都在下面说明。
请求在哪里运行,就由哪套规则决定
就网络而言,content script 与页面共享同一个 origin。主机权限在那里并不能解除 CORS。这是最常见的一个意外。
把请求挪到 background
可靠的做法是让 background 去 fetch,再把结果送回来:src/content/scripts.js
src/background.js
声明主机权限
background 只有对 manifest 点名的 origin 才豁免 CORS:manifest.json
<all_urls> 能用,但商店审核者会追问它。
预检请求依然会发生
主机权限去掉的是对响应的 origin 检查,它去不掉预检。 带自定义请求头的请求,或者用了GET、HEAD、POST 之外方法的请求,仍然会先发出一个 OPTIONS 请求。服务器必须回应它。服务器在你手上时,就放行你发出的方法和请求头。不在你手上时,就把请求保持简单。
Extension.js 开发服务器
在extension dev 期间,Extension.js 会运行一个本地服务器,用来提供重载客户端和热更新。任何页面上的 content script 都会拨向它,这本身就是一次跨源请求,所以服务器答以:
Host 请求头。这是一个跑在你自己机器上的开发服务器。它永远不会出现在打包后的扩展里,它提供的任何东西也不会进入生产环境。
仅在开发期生效的 CSP 补丁
除非你自己设置,否则 Manifest v3 不会限制connect-src。因此一个没有声明 content_security_policy 的项目不需要任何补丁,开发用的 manifest 里带的就是普通策略:
dist/chromium/manifest.json
connect-src,开发构建就会把本地 origin 追加进去,这样你的页面仍然连得上那个 socket:
dist/chromium/manifest.json
extension build 时被移除,你自己的 connect-src 则会保留。所以一份漏掉了你 API origin 的严格策略,会在开发期通过、在生产环境失败。当一个 CSP 报错一路活到生产环境时,请阅读 Manifest 拒绝。
绑定到另一个主机
在容器里开发时,把服务器绑到别处:0.0.0.0,所以 Extension.js 会为客户端解析出一个可连接的地址,兜底值是 127.0.0.1。当浏览器在另一台机器上时,请覆盖它:
远程脚本与样式表
扩展的 CSP 禁止把远程 origin 上的脚本加载进扩展页面。那不是 CORS,任何响应头都修不好它。请改为把代码打进包里。 Extension.js 会在构建期报告这种情况:症状与修复
最佳实践
- 把每一个第三方请求都放进 background,包括今天还能正常工作的那些。
- 在
host_permissions里逐个点名 origin,而不是伸手去拿<all_urls>。 - 发布之前先用生产构建测一遍。开发期更宽松是有意为之。

