指南 / Web

CORS:浏览器知道 API 就在那里,但就是不让你碰。

API 在 curl 或 Postman 能请求,浏览器却偏偏拦住。CORS 不是普通网络故障,而是浏览器关于“哪个来源可以读取哪些响应”的安全规则。

发布 · 更新 · 10 MIN

前端写好了。

API 写好了。

两个都在线。

它们单独工作都很正常。

于是按照互联网传统,它们拒绝互相说话。

控制台丢下一句:

blocked by CORS policy

服务器明明存在,URL 也能打开,甚至响应可能已经回来了,浏览器还是不让你的 JavaScript 读取。

这就是 CORS。

先搞懂 Origin

CORS 是 Cross-Origin Resource Sharing,跨源资源共享。所谓 Origin,可以粗略理解成:

协议 + 主机名 + 端口

https://example.com 与 https://api.example.com 是不同 Origin。http://example.com 与 https://example.com 也不同。端口不同,同样算不同。

人类看着都像“我自己的网站”,浏览器看着则像三个街区,各自有保安。

为什么浏览器要多管闲事

假设你登录着邮箱或后台,又打开了一个陌生网站。那个页面同样可以运行 JavaScript。如果浏览器允许任何页面随便读取其他站点的响应,恶意页面就可能尝试利用你现有的登录状态访问敏感内容。

CORS 并不是整个 Web 安全模型,但它是浏览器跨源访问控制里很重要的一层。

最关键的一点是:CORS 主要由浏览器执行。

“可是在 Postman 里明明能用”

这恰恰很正常。Postman 和 curl 不是运行在普通网页的同源安全模型里。

curl https://api.example.com/data

能成功,而:

fetch("https://api.example.com/data")

失败,并不意味着服务器一定挂了。更可能是浏览器在说:服务器没有明确告诉我,这个网页有权读取它的响应。

最常见的响应头

Access-Control-Allow-Origin: https://app.example.com

它相当于服务器写了一张许可:这个 Origin 可以读取响应。

完全公开的资源有时会使用:

Access-Control-Allow-Origin: *

很方便,但不是所有场景都应该这么写。尤其当请求带 Cookie 或其他凭据时,规则会更严格。

OPTIONS 为什么突然冒出来

你明明写的是 POST,Network 面板却先出现一个 OPTIONS。这通常是 preflight,预检请求。

浏览器先问服务器:

“我等会儿想用这个方法、这些请求头,从这个 Origin 来访问,你同意吗?”

服务器需要用类似下面的响应头回答:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization

预检失败时,真正的 POST 甚至不会发送。这也是为什么你翻后端日志,发现“根本没有收到请求”。POST 还在门口排队,OPTIONS 已经和保安吵起来了。

mode: "no-cors" 通常不是解决方案

很多人第一次搜索报错,会看到:

fetch(url, { mode: "no-cors" })

报错似乎不见了,但响应通常变成 opaque,你的 JavaScript 不能像原来一样读正文。换句话说,你只是告诉浏览器:

“行,发出去吧,但回来之后也别让我看。”

某些场景有用,但绝不是“关闭 CORS”按钮。

带 Cookie 时更严格

如果请求使用:

credentials: "include"

服务端通常还要:

Access-Control-Allow-Credentials: true

并且不能再随便用通配符 Origin。认证请求值得比“全互联网都来吧”更谨慎一点。

CORS 也不是身份认证

只允许某个前端域名,并不意味着 API 已经安全。curl、脚本和其他非浏览器客户端仍然可以直接请求后端。

真正的私有数据仍然需要认证和授权:Session、Token、权限判断、API Key 或其他访问控制。

CORS 管的是浏览器能不能把跨源响应交给网页脚本。认证授权管的是这个请求本身有没有资格拿到数据。两个岗位,不要让其中一个替另一个上班。

我排 CORS 的顺序

  1. 请求有没有到服务器。
  2. 有没有 OPTIONS 预检。
  3. 预检状态码是什么。
  4. Access-Control-Allow-Origin 是否存在且完全匹配。
  5. 方法是否允许。
  6. 自定义请求头是否允许。
  7. 是否用了 credentials。
  8. 中途是否发生重定向。
  9. CDN、Worker 或代理是否改写了响应头。

Console 告诉你“有事发生”,Network 才更像现场记录。

最后

CORS 第一次出现时很像浏览器故意找茬:API 在,服务器也回了,偏偏不让你拿。

但理解 Origin、预检和响应头之后,它就没那么神秘。

浏览器不是忘了怎么联网。

它只是说:

“我知道 API 在哪。我也知道它回了什么。”

“不过在把内容交给这段 JavaScript 之前,我想先看看书面许可。”