同源策略与 CORS 预检
「跨域报错」几乎是每个前端必遇的问题,但很多人只背了解法,没理解决策方是谁。
同源策略拦的是「读」,不是「发」
同源 = 协议、域名、端口三者一致。浏览器的规则是:跨源请求可以发出,服务器也会处理,但响应对页面脚本不可读。所以 CSRF 依然存在(请求到达了),而 XSS 偷不到别的域的数据——同源策略保护的是读。
<img>、<script>、form 提交不受此限,这正是 JSONP(利用 script 标签)和 CSRF(利用 form/img)存在的原因。
CORS 的机制
服务器通过响应头声明允许谁读:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, X-CSRF-Token
Access-Control-Max-Age: 600
预检(Preflight)
带自定义头、非简单方法(PUT/DELETE)、Content-Type: application/json 的请求,浏览器先发 OPTIONS 询问服务器「允不允许」。允许则发真请求,不允许则真请求根本不发出。
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
return 204;
}
proxy_pass http://backend;
}
Access-Control-Max-Age 让浏览器缓存预检结果,减少 OPTIONS 次数。
三个高频错误
1. * 与凭证互斥。 带 Cookie 的跨域(credentials: 'include'),Allow-Origin 必须是具体域名且 Allow-Credentials: true,通配符直接失败。
2. 响应头没配在正确的层。 代理和后端各配了一遍,出现重复头,浏览器视为无效。配一层就够,通常在网关。
3. 把 CORS 当成防火墙。 它只约束浏览器里的 JS。curl、Postman、服务端调用完全无视它——CORS 不是接口的访问控制,鉴权还是要做。
与反向代理的对比
同域部署(前端静态 + /api 反代)可以直接绕开 CORS,是最省心的架构。代价是部署耦合。前后端完全分离时才需要认真配 CORS——此时把允许的域名列表做成配置,而不是散在代码里。