CORS 错误困扰你?这个决策树搞定一切

作者: Trove Deck Solution 发布: 2026-05-09 阅读时长: 7 分钟

你的暂存服务器可以访问你的 API。一切正常。等上线到生产环境,突然:Access to XMLHttpRequest at 'https://api.yoursite.com' from origin 'https://yoursite.com' has been blocked by CORS policy.

代码没变。完全一样的代码。但它现在在尖叫 CORS 错误。

这里是大多数独立开发者迷失两小时的地方。

CORS 为什么存在(30秒速览)

浏览器强制执行 CORS(跨源资源共享)来阻止恶意网站窃取你的数据。如果一个流氓网站能随意访问你的 API,攻击者就能掏空钱包、窃取 PII、泄露数据库——经典噩梦。CORS 是守卫。它问:”你是谁?”

问题是:往往你就是那个请求者,守卫就是认不出你。

真正有用的决策树

在你开始乱加 headers 之前,问自己三个问题:

1. 请求跨越源了吗? - 相同协议(http vs https)?相同域名(app.site.com vs api.site.com)?相同端口(8000 vs 8001)? - 任何一个不同 → 你有 CORS 问题。都相同 → CORS 不是你的麻烦(检查控制台找真实错误)。

2. 这是 preflight 请求(OPTIONS)吗? - 浏览器在发送真实的 POST/PUT/DELETE 之前自动发送一个 OPTIONS 请求。 - 如果你的服务器不响应 OPTIONS,preflight 失败,真实请求永不出发。 - 修复:你的后端必须用正确的 headers 响应 OPTIONS。

3. 你的后端发送了对的 headers 吗?

Access-Control-Allow-Origin: https://yourfrontend.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

哪怕一个错或缺少 → CORS 被挡。没有例外。

场景 1:本地开发(端口不匹配)

前端在 http://localhost:3000,API 在 http://localhost:3001

为什么坏了:不同端口 = 不同源。

修复: - 选项 A:通过 Next.js rewrites 或 Vite 中间件代理请求(localhost:3000/api/...localhost:3001/...)。零 CORS 烦恼。 - 选项 B:给后端加 Access-Control-Allow-Origin: http://localhost:3000

专业提示:选项 A 更干净。一个域名,零 CORS。这就是为什么 Next.js API 路由存在。

场景 2:暂存 + 生产不匹配

暂存能工作是因为前端和 API 都在同一域名(子域名算跨源)。生产坏了是因为域名改了或用了 CDN。

为什么坏了:你后端的 Access-Control-Allow-Origin header 被硬编码成旧域名。

修复:让 header 动态化。

不要硬编码,而是白名单源:

const allowedOrigins = ['https://yoursite.com', 'https://staging.yoursite.com', 'http://localhost:3000'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
  res.set('Access-Control-Allow-Origin', origin);
}

场景 3:第三方 API(你改不了他们的 CORS)

你从浏览器调用 Stripe、Twilio 或某个第三方 API。他们没设 Access-Control-Allow-Origin: *,你的请求就死了。

为什么坏了:他们的服务器不允许来自你域名的浏览器请求。

修复:别从浏览器调。从你的后端调。

坏:从 React 里 fetch('https://api.stripe.com/...')。 好:从 React 里 fetch('/api/stripe-charge') → 后端调 Stripe。

你的后端不受 CORS 约束。它可以和谁都说话。

场景 4:凭证和 Cookie

你在 fetch 里用 credentials: 'include' 发送 auth token,但 API 没设 Access-Control-Allow-Credentials: true

为什么坏了:浏览器阻止带凭证的请求,除非服务器明确说可以。

修复:加上 header,把 Access-Control-Allow-Origin 改成具体的(不是 *)。

如果用 Access-Control-Allow-Origin: * 搭配凭证,浏览器会拒绝。似乎矛盾,但这是故意的。

调试清单

  1. 打开开发者工具 Network 标签。找到失败的请求。
  2. 点击它。看 Response Headers。有 Access-Control-Allow-Origin 吗?
  3. 它包括你前端的源吗?
  4. 是 OPTIONS 请求吗?检查 200 响应有没有 CORS headers。
  5. 请求是 POST/PUT/DELETE 且带自定义 headers 或 Content-Type: application/json 吗?确保 Access-Control-Allow-Headers 包含它们。

缺一个 header = 请求被挡。

什么时候会变复杂

CORS 在理论上很简单。实际上,配置不当的 CORS 会在生产部署、暂存/生产分歧、第三方集成中闹鬼。你会在不同的代码路径上诊断三次同一个问题,每次根本原因都不同。

如果你的 API 跨越多个服务、多个团队或动态生成的源,CORS 卫生就成了一个大项目。如果在生产里搞砸,用户看得见——不是安静的 bug,而是坏掉的功能。

这就是在初期搞对后端架构的重要性(加上认证、错误处理、版本管理)能为你省掉多少小时的救火。无论你是自己造 API 还是找专家帮忙,在一开始就把 CORS 搞对(连同整个栈)会节省大量时间。

如果你在开发一个涉及敏感用户数据或外部服务集成的产品,而 CORS 总是咬你一口,可能是时候从更整体的角度思考你的后端策略了。Trove Deck Solution 和 SaaS 创始人及独立开发者合作,设计不会坏掉的后端——API 安全、CORS、认证、整个栈——并正确上线。来聊聊吧。

#CORS#WebDevelopment#APIDebugging#SaaS#TechTroubleshooting#IndieHackers#BackendDevelopment