如果你正在学前端开发或者调用API,大概率见过这类报错——"Blocked by CORS policy"、"No 'Access-Control-Allow-Origin' header"、"Cross-origin request blocked"。第一次碰到时,确实像浏览器莫名其妙发了脾气。但CORS并非随机拦截,它是一套有明确目的的浏览器安全规则。

一句话概括:CORS是浏览器用来控制"网页何时能向不同来源发送请求"的规则。这里的"来源"(origin)由三部分组成:协议、域名、端口。以下这些看似相关的地址,浏览器会视为完全不同的来源:

打开网易新闻 查看精彩图片

https://myapp.com
http://myapp.com
https://api.myapp.com
https://myapp.com:3000

只要其中任何一部分不同,就是跨来源请求。假设你的前端运行在 https://myfrontend.com,试图从 https://api.other-site.com/data 获取数据,浏览器会立即识别出这是跨来源操作,然后检查目标服务器是否允许。若服务器未授权,浏览器直接阻断响应。

CORS存在的根本原因是保护用户。没有这类规则,恶意网站就能利用你当前的登录状态、Cookie或会话信息,悄悄向其他网站发起请求。CORS让服务器明确表态:哪些网站可以读取响应,哪些不行。它不是故意为难开发者,而是浏览器基于安全风险的必要戒备。

理解CORS需要先知道同源策略(Same-Origin Policy)——浏览器默认禁止页面自由读取不同来源的响应。CORS则是服务器主动放宽这一限制的机制。简单说:同源策略是默认锁,CORS是服务器掌握的钥匙。

服务器通过特定HTTP头来授权。最常见的是 Access-Control-Allow-Origin: https://myfrontend.com,表示允许该特定来源访问。有时也会看到 Access-Control-Allow-Origin: *,意为"任何来源均可",这对完全公开的资源适用,但涉及身份验证时未必合适。

一个容易混淆的点:CORS主要是浏览器的行为。服务器只负责发送响应头,最终决定是否让前端JavaScript读取响应的,是浏览器。即便服务器返回了200状态码和数据,若缺少正确的CORS头,浏览器仍会拦截,前端代码拿不到任何内容。

这对调试很棘手——网络面板能看到响应,控制台却报错,因为浏览器在"最后一公里"拦下了数据。这不是bug,是CORS的工作方式。

常见场景下,开发者遇到的CORS问题通常有几种解决路径:后端添加正确的响应头、使用代理服务器转发请求、或者调整请求配置(如withCredentials与Access-Control-Allow-Credentials的配合)。但核心原则不变:CORS的决策权在服务器,执行权在浏览器。

另一个细节是预检请求(Preflight)。对于某些HTTP方法(如PUT、DELETE)或自定义头,浏览器会先发送OPTIONS请求试探服务器态度,确认允许后再发正式请求。这解释了为什么有时候控制台显示两个请求——第一个是"问路"的,第二个才是"办事"的。

理解CORS的关键是分清责任边界:服务器声明政策,浏览器强制执行。开发者常犯的错误是试图在前端"解决"CORS——加什么请求头、改什么fetch配置——但根本解方几乎总是在服务器端。浏览器不会因为前端代码的意愿而放宽安全策略。

这也解释了为什么本地开发时常遇CORS问题:localhost与生产环境的来源不同,而许多后端服务默认只允许特定域名访问。开发环境的代理配置、浏览器插件临时禁用CORS,都是权宜之计,不能用于生产。

从产品设计角度看,CORS体现了Web安全的一个基本张力:开放与隔离的平衡。浏览器既要让网页能够组合各类服务( mashup ),又要防止恶意站点窃取用户在其他站点的身份。CORS是这种平衡的技术实现——不是最优雅的方案,但是在现有架构下务实的折中。

对于初学者,遇到CORS报错时的排查思路可以简化为三步:确认请求来源与目标是否跨域、检查服务器响应头是否包含Access-Control-Allow-Origin、验证预检请求是否通过。多数问题都能在这三步内定位。

最后值得强调的是,CORS只约束浏览器环境。服务器之间的HTTP请求、curl命令行调用、Postman等工具都不受CORS限制——它们没有"用户会话被恶意利用"的风险场景。这也是为什么同样的API在Postman里工作正常,放到浏览器里却报错,这恰恰说明CORS在发挥作用。