那个 CORS 错误不是 bug —— 它实际上在保护你的 Web 应用
Source: Dev.to
如果你在 Web 应用中使用过 API,可能至少见过一次以下错误:
Access to fetch has been blocked by CORS policy
这让人很恼火:
- API 地址是正确的。
- 代码看起来也没有问题。
但浏览器就是不配合。
起初,这看起来像是一个 bug 或者配置错误。事实上:
CORS 并不是错误——它是一项安全特性,正是按照预期在工作。
在本文中,我们将用通俗的语言了解 CORS,看看它为何存在、解决了哪些问题,以及浏览器为何会有这样的表现。
1. 为什么最初会有 CORS
要理解 CORS,先想象一个 没有 CORS 的情形。

假设你已经登录了:
facebook.com- 或者你的银行网站,例如
hdfc.com
你的浏览器会保存 cookie 或 token,以保持登录状态。
现在,在另一个标签页中,你打开了一个随机(可能是恶意的)网站。该站点同样会运行 JavaScript。
如果 没有任何限制,这段 JavaScript 可以发送如下请求:
fetch("https://hdfc.com/api/balance")
因为请求是从你的浏览器发出的,你的银行 cookie 会自动随请求一起发送。
银行服务器会认为:
“这个请求来自已登录的用户。”
于是可能返回诸如你的账户余额等敏感数据。
这非常危险。
为防止这种情况,浏览器实现了 同源策略(Same‑Origin Policy,SOP):
- 默认情况下,网站只能访问同源的数据。
CORS(跨源资源共享) 是一种受控的方式来放宽这条规则——但前提是服务器明确允许。
2. 什么是“源”?
一个源由三部分组成:
| 部分 | 示例 |
|---|---|
| 协议 | http 或 https |
| 主机 | 域名 (example.com) |
| 端口 | 80、443、5173 等 |
如果任意一项发生变化,则视为不同的源。
示例
| 源 A | 源 B | 相同? |
|---|---|---|
https://yuktisahu.dev | https://api.yuktisahu.dev | ❌(不同的主机) |
https://yuktisahu.dev | http://yuktisahu.dev | ❌(不同的协议) |
http://localhost:5173 | http://localhost:8000 | ❌(不同的端口) |
https://example.com/page1 | https://example.com/page2 | ✅(相同源——路径无关) |
浏览器使用此定义来判断请求是否跨源。
3. 为什么不能在前端修复 CORS
这是初学者最容易感到困惑的部分之一。
你在浏览器控制台看到错误,于是尝试在前端代码中修复它。
但 CORS 并不是由前端代码控制的。
实际发生的情况
-
你的前端向另一个源发送请求。
-
浏览器会自动添加
Origin头。 -
服务器检查这个
Origin。 -
如果服务器信任该来源,它会在响应中加入以下头部:
Access-Control-Allow-Origin: https://yuktisahu.dev -
如果该头部缺失或与您的来源不匹配,浏览器会阻止响应,您的 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-urlencoded、multipart/form-data或text/plain)
其他请求被视为“复杂的”,例如:
PUT、PATCH、DELETE- 带有自定义头部的请求
- 使用
Content-Type: application/json的请求
对于这些请求,浏览器会先发送一个额外的请求:
OPTIONS /api
这被称为 预检请求。它会询问服务器:
“如果我使用这些头部发送这种类型的请求,可以吗?”
如果服务器返回了相应的 CORS 头部,浏览器就会继续发送实际请求。否则,浏览器会完全阻止该请求。
最后思考
CORS 让人感到沮丧,因为它会阻止你的代码,但它的存在是为了保护:
- 已登录用户
- 敏感数据
- 浏览器安全边界
一旦你理解了这一点:
- CORS 错误更容易理解。
- 调试变得更容易。
- 你会知道解决方案总是在 服务器端。
所以,下次看到 CORS 错误时,不要尝试在 JavaScript 中“修复”,而是检查服务器的 CORS 配置(或在开发期间使用代理),这样就能恢复正常。
想到 “有什么东西坏了” 时,应该想到:
“浏览器正在保护用户——而服务器尚未授予权限。”