跨域的根本问题
先假设一个危险的情况:网站 A 可以随意读取网站 B 的响应。
用户已经登录银行,浏览器可能带着银行的 Cookie。此时,恶意网页只需要调用银行的余额、转账记录接口,再把响应发给攻击者,用户的敏感数据就泄露了。
所以,浏览器的**同源策略(Same-Origin Policy)**存在的核心目的,是阻断这种跨网站读取敏感数据的行为。
而 CORS(Cross-Origin Resource Sharing),则是服务器向浏览器声明的、受规则约束的例外机制:服务器可以明确告诉浏览器,哪些其他来源的网页可以读取自己的响应。
什么叫"同源"?
一个源由三部分共同组成:
Origin = 协议 + 主机 + 端口

这三者只要有一个不同,就是不同源:
text
http://localhost:5173
-> http://localhost:3000 不同端口,跨源
http://localhost:3000
-> https://localhost:3000 不同协议,跨源
http://localhost:3000
-> http://127.0.0.1:3000 不同主机,跨源
跨域不等于请求绝对发不出去
浏览器需要允许很多跨源资源加载,否则网页根本无法使用图片、样式表或跳转链接。
真正受保护的重点是:

所以,看到 CORS 报错时,更准确的理解是:
浏览器不允许当前网页的 JavaScript 读取这次跨源响应。
这句话很重要。CORS 不等于请求一定没有到达服务器。 对于一些请求,浏览器可能会发送请求,但把响应拦在网页 JavaScript 之外。
这也是为什么 CORS 不能当作后端的认证、授权或 CSRF 防护。服务端仍然必须自己验证用户身份、权限和请求是否合法。
CORS 如何表达"同意"?
当网页 A 请求服务器 B 时,浏览器会带上请求来源信息:
http
Origin: https://a.example
如果服务器 B 允许 https://a.example 的网页读取响应,应在响应中返回:
http
Access-Control-Allow-Origin: https://a.example
Access-Control-Allow-Origin 的含义是:
允许来自
https://a.example的网页读取这次响应。
其因果链如下:

所以,浏览器不是在信任网页 A,而是在执行服务器 B 对网页 A 做出的授权声明。
但这里的"授权"只针对浏览器能否把响应交给网页 JavaScript,不替代业务层面的登录认证和权限判断。
预检请求
并不是所有跨源请求都需要服务器先允许。
像有些满足 CORS"简单请求"条件的请求,浏览器会直接发送;而有些请求不满足这些条件,浏览器会先发送一个 OPTIONS 请求询问目标服务器。这个"先问"的行为,就叫预检请求(Preflight Request)。
这里不是浏览器判断"这个业务风险高不高",而是浏览器根据方法、请求头和 Content-Type 是否属于 CORS safelist 来决定是否预检。

预检通常会询问三件事:
http
Origin: https://a.example
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-type
服务器必须分别回应:
- 是否允许这个来源。
- 是否允许这个 HTTP 方法。
- 是否允许这些额外请求头。
例如:
http
Access-Control-Allow-Origin: https://a.example
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
像 PUT、DELETE、Authorization 请求头、application/json 等情况,通常会触发预检。
预检的目的在于:
让目标服务器先声明:浏览器是否可以从这个源,以这个方法、带着这些请求头,发起实际跨源请求并读取响应。
即使预检通过,服务器在真正收到实际请求后,仍然必须完成认证、授权和业务校验。
一个完整的跨域请求
1. 预检请求

浏览器先发送 OPTIONS。如果服务器返回的 CORS 响应头满足要求,浏览器才继续发送实际请求。
2. 实际请求

实际响应也需要包含相应的 CORS 响应头,浏览器才会把响应内容交给网页 JavaScript。
跨域请求想携带 Cookie,还需要什么?
跨源请求是否携带 Cookie,不是只配置 Access-Control-Allow-Origin 就够了。
前端需要显式声明:
js
fetch('https://api.example.com/orders', {
credentials: 'include'
})
服务端则需要返回:
http
Access-Control-Allow-Origin: https://a.example
Access-Control-Allow-Credentials: true
命令行或后端服务不受 CORS 影响
如果攻击者不是从浏览器调用,而是使用命令行程序或后端服务,则不会遇到 CORS 报错。
因为 CORS 从一开始就是浏览器的同源策略机制。curl、后端服务不受浏览器规则约束;它们能否访问接口,仍应该由服务端的身份认证、权限授权和网络安全策略决定。
总结
同源策略默认不让网页 JavaScript 读取跨源响应;CORS 是服务器授予特定来源读取权限的声明;它不是服务端安全边界,更不能替代认证、授权和 CSRF 防护。