同源策略与 CORS
为什么“跨域”问题总被学得又熟又乱
很多前端工程师都见过跨域报错。
也大概知道要配:
Access-Control-Allow-Origin
但这类问题常常停留在“知道怎么救火”,没有形成稳定模型。
原因很简单。
很多资料一上来就讲 CORS 头。
却没有先把同源策略讲清楚。
先把同源策略讲清楚
同源策略可以先理解成:
浏览器默认限制一个源里的文档或脚本随意读取另一个源里的受保护内容。
这里最关键的是:
这是浏览器的安全边界之一。
不是网络世界里天然存在的物理限制。
什么叫“同源”
通常指:
- 协议相同
- 主机相同
- 端口相同
这三个共同决定一个 origin。
只要其中关键部分不同,就可能是不同源。
同源策略真正想防什么
它的核心目标之一是:
避免一个站点里的脚本,轻易读取另一个站点中用户上下文下的敏感内容。
如果没有这层限制,很多跨站攻击会变得更容易。
所以你不能把它理解成“浏览器故意为难前端”。
它首先是安全设计。
为什么“能发请求”和“能读响应”不是一回事
这是理解 CORS 的关键一步。
浏览器里,某些跨源请求并不是完全发不出去。
真正被重点限制的,往往是:
脚本是否能访问响应内容。
如果这点没想清楚,很多跨域讨论都会混乱。
CORS 到底是什么
CORS 是 Cross-Origin Resource Sharing。
更稳妥的理解是:
它是一套基于 HTTP 头的机制,让服务端告诉浏览器:哪些跨源访问可以被脚本安全读取。
也就是说,CORS 不是“取消同源策略”。
它是同源策略之上的有条件放行。
为什么 CORS 一定要强调“浏览器”
因为它主要是浏览器执行的安全模型。
服务器和服务器之间直接通信,不会按浏览器 CORS 逻辑来限制。
这也是为什么很多人会说:
- Postman 请求能成功
- 浏览器里却报 CORS
这并不矛盾。
因为执行环境根本不同。
Access-Control-Allow-Origin 应该怎么理解
它是服务端向浏览器表达:
这个响应允许被哪些源的脚本读取。
但这里最常见的误区是把它当成万能开关。
现实里它还要和:
- credentials
- 预检请求
- 允许方法
- 允许头部
一起看。
什么是 preflight
preflight 可以先理解成:
正式跨源请求前,浏览器先发一个探测请求,确认目标服务是否允许这种跨源访问方式。
这通常是一个 OPTIONS 请求。
它不是多余折腾。
它是在更危险或更复杂的跨源条件下增加一次显式确认。
为什么有些请求不触发 preflight
因为浏览器对“相对简单”的跨源请求有一套更宽松的处理条件。
当请求方法、头部和内容类型都在某些简单范围内时,通常不会先走预检。
但一旦超出条件,就更可能触发 preflight。
这就是为什么团队里常会出现:
- 明明都是跨域
- 为什么这个会多一次 OPTIONS
credentials 为什么总和 CORS 一起出现
因为一旦跨源请求带上:
- cookie
- HTTP 认证信息
风险语境就更敏感。
这时:
- 客户端要显式选择是否带 credentials
- 服务端也要明确允许
而且还要注意:
当允许 credentials 时,Access-Control-Allow-Origin 不能再随便用通配符表达一切。
为什么很多人把 CORS 问题归咎于前端
因为报错出现在浏览器控制台。
但本质上,CORS 是前后端边界共同定义的问题。
前端要知道:
- 请求发成了什么样
- 有没有触发预检
- 有没有错误带上凭据
后端要知道:
- 允许哪些源
- 允许哪些头和方法
- 凭据策略是什么
如果任一侧认知模糊,CORS 问题就会反复出现。
同源策略和 CORS 不是全部跨源安全问题
这点也要讲清楚。
它们主要讨论的是:
- 浏览器脚本能不能跨源读响应
但安全世界里还有很多相关问题:
- CSRF
- XSS
- Cookie 策略
- SameSite
如果把这些完全混在一起,反而会更乱。
更好的方式是:
先明确每套机制各自解决什么问题。
为什么代理方案经常能“绕过跨域”
因为代理会改变请求路径上的源关系。
从浏览器视角看,当前页面也许是在请求同源的本地开发服务器。
再由这个代理去和目标服务通信。
所以这不是“破解浏览器限制”。
而是改变了浏览器看到的架构。
本地开发里的跨域为什么特别常见
因为:
- 前端 dev server 在一个端口
- API 服务在另一个端口
这天然就是不同源。
所以开发代理在前端工程里非常常见。
但到了生产环境,真正解决问题的仍然应是明确的服务端策略与部署架构。
CORS 最常见的几个误区
1. 以为服务端只要加一个通配符就万事大吉
真实系统还要看 credentials 与安全边界。
2. 以为 CORS 是后端问题,前端不用懂
前端如果不理解 preflight 和 credentials,排障会非常被动。
3. 以为跨域请求根本发不出去
很多情况下问题在于脚本不能读取响应。
4. 把 CORS、CSRF、cookie 安全策略讲成一回事
这些主题相关,但不是同一个机制。
中国互联网语境里的现实特点
很多国内项目会同时面对:
- 多域名体系
- 网关与 BFF
- 小程序 / WebView / H5 混合环境
- 登录态复杂
这会让跨源问题不仅是浏览器报错。
还会和:
- cookie 策略
- 代理层
- 网关配置
- 调试环境差异
一起出现。
海外语境里,为什么更常把 CORS 放回 Web 平台安全模型里讲
很多英文资料会更强调:
- same-origin policy
- credentialed requests
- preflight
- fetch 模型
这非常有帮助。
因为它能把“配置头部”重新拉回“浏览器安全机制”的语境。
这类主题为什么很适合做表达训练
因为它太容易被讲成:
- 跨域就是后端没配头
这太浅了。
更好的表达应该能继续说明:
- 同源策略为什么存在
- CORS 为什么是受控放行
- 为什么能发请求不等于能读响应
- preflight 和 credentials 分别在解决什么
建议实践
实践 1:做一个最小 CORS 实验
练什么:
建立浏览器跨源读取模型。
最小交付物:
一个前端页面加一个最小 API 服务。
验收标准:
- 能复现阻止读取响应的情况
- 能通过正确头部配置恢复访问
常见误区:
- 只会改头,不解释机制
实践 2:对照观察 preflight
练什么:
理解为什么有时会多一次 OPTIONS。
最小交付物:
一组 Network 面板记录。
验收标准:
- 能指出哪个条件触发了预检
- 能解释预检失败意味着什么
常见误区:
- 把预检当成“浏览器多发了一次没意义的请求”
实践 3:比较代理方案与服务端 CORS 配置
练什么:
区分开发便利和生产边界。
最小交付物:
一份开发代理与正式跨源配置的对照说明。
验收标准:
- 能说明代理为什么改变了浏览器看到的源关系
- 能说明为什么它不等于生产级安全方案
常见误区:
- 把 dev proxy 当成真正的跨域治理方案