那个 CORS 错误不是 bug —— 它实际上在保护你的 Web 应用

发布: (2025年12月27日 GMT+8 13:37)
7 min read
原文: Dev.to

Source: Dev.to

如果你在 Web 应用中使用过 API,可能至少见过一次以下错误:

Access to fetch has been blocked by CORS policy

这让人很恼火:

  • API 地址是正确的。
  • 代码看起来也没有问题。

但浏览器就是不配合。

起初,这看起来像是一个 bug 或者配置错误。事实上:

CORS 并不是错误——它是一项安全特性,正是按照预期在工作。

在本文中,我们将用通俗的语言了解 CORS,看看它为何存在、解决了哪些问题,以及浏览器为何会有这样的表现。

1. 为什么最初会有 CORS

要理解 CORS,先想象一个 没有 CORS 的情形。

Diagram of the CORS flow and explanations

假设你已经登录了:

  • facebook.com
  • 或者你的银行网站,例如 hdfc.com

你的浏览器会保存 cookie 或 token,以保持登录状态。

现在,在另一个标签页中,你打开了一个随机(可能是恶意的)网站。该站点同样会运行 JavaScript。

如果 没有任何限制,这段 JavaScript 可以发送如下请求:

fetch("https://hdfc.com/api/balance")

因为请求是从你的浏览器发出的,你的银行 cookie 会自动随请求一起发送。
银行服务器会认为:

“这个请求来自已登录的用户。”

于是可能返回诸如你的账户余额等敏感数据。

这非常危险。

为防止这种情况,浏览器实现了 同源策略(Same‑Origin Policy,SOP)

  • 默认情况下,网站只能访问同源的数据。

CORS(跨源资源共享) 是一种受控的方式来放宽这条规则——但前提是服务器明确允许。

2. 什么是“源”?

一个源由三部分组成:

部分示例
协议httphttps
主机域名 (example.com)
端口804435173

如果任意一项发生变化,则视为不同的源。

示例

源 A源 B相同?
https://yuktisahu.devhttps://api.yuktisahu.dev❌(不同的主机)
https://yuktisahu.devhttp://yuktisahu.dev❌(不同的协议)
http://localhost:5173http://localhost:8000❌(不同的端口)
https://example.com/page1https://example.com/page2✅(相同源——路径无关)

浏览器使用此定义来判断请求是否跨源。

3. 为什么不能在前端修复 CORS

这是初学者最容易感到困惑的部分之一。

你在浏览器控制台看到错误,于是尝试在前端代码中修复它。
CORS 并不是由前端代码控制的

实际发生的情况

  1. 你的前端向另一个源发送请求。

  2. 浏览器会自动添加 Origin 头。

  3. 服务器检查这个 Origin

  4. 如果服务器信任该来源,它会在响应中加入以下头部:

    Access-Control-Allow-Origin: https://yuktisahu.dev
  5. 如果该头部缺失或与您的来源不匹配,浏览器会阻止响应,您的 JavaScript 永远得不到它。

因此,即使服务器已经返回了响应,浏览器也会拒绝将该响应交给您的代码。

这就是为什么 CORS 必须在服务器端解决,而不是在前端。

4. 为什么在 Postman 能用而在浏览器里不行

另一个常见的困惑:

“API 在 Postman 中可以正常工作,为什么在我的 Web 应用中会失败?”

因为 CORS 是浏览器的安全规则,而不是服务器的规则

工具它如何处理 CORS
Postman(或 curl不是浏览器 → 不会强制执行 CORS,也不会携带其他站点的 Cookie。
浏览器同时运行来自多个站点的代码,为敏感站点存储 Cookie,必须保护用户免受数据泄漏。

5. Cookies 使 CORS 更严格

有时前端需要在请求中发送 cookie:

fetch(url, {
  credentials: "include"
})

当这样做时,规则会更严格:

  • 服务器 不能 使用通配符响应:

    Access-Control-Allow-Origin: *
  • 必须显式指定允许的来源并且允许凭证:

    Access-Control-Allow-Origin: https://yuktisahu.dev
    Access-Control-Allow-Credentials: true

这是一种有意的设计。
如果涉及 cookie,服务器必须明确信任 单一特定来源,而不是所有来源。

6. 什么是预检请求?

一些请求是“简单的”,例如:

  • GET
  • 基本的 POST(使用 Content-Type: application/x-www-form-urlencodedmultipart/form-datatext/plain

其他请求被视为“复杂的”,例如:

  • PUTPATCHDELETE
  • 带有自定义头部的请求
  • 使用 Content-Type: application/json 的请求

对于这些请求,浏览器会先发送一个额外的请求:

OPTIONS /api

这被称为 预检请求。它会询问服务器:

“如果我使用这些头部发送这种类型的请求,可以吗?”

如果服务器返回了相应的 CORS 头部,浏览器就会继续发送实际请求。否则,浏览器会完全阻止该请求。

最后思考

CORS 让人感到沮丧,因为它会阻止你的代码,但它的存在是为了保护:

  • 已登录用户
  • 敏感数据
  • 浏览器安全边界

一旦你理解了这一点:

  • CORS 错误更容易理解。
  • 调试变得更容易。
  • 你会知道解决方案总是在 服务器端

所以,下次看到 CORS 错误时,不要尝试在 JavaScript 中“修复”,而是检查服务器的 CORS 配置(或在开发期间使用代理),这样就能恢复正常。

想到 “有什么东西坏了” 时,应该想到:

“浏览器正在保护用户——而服务器尚未授予权限。”

Back to Blog

相关文章

阅读更多 »

Academic Suite 身份验证与授权

3.1 Academic Suite 中的身份验证方法 Academic Suite 使用基于 JSON Web Token(JWT)的无状态身份验证方法。不同于基于会话的身份验证…