同源策略

同源(Same Origin):协议、域名、端口三者完全相同。

https://example.com:443/page
https://example.com/api     → 同源
http://example.com/api      → 不同(协议不同)
https://api.example.com     → 不同(域名不同)
https://example.com:8080    → 不同(端口不同)

同源策略限制:一个源的脚本不能读取另一个源的资源(如通过 AJAX 读取响应内容)。

目的:防止恶意网站读取你在其它网站的敏感数据。

举例:你登录了银行网站,然后访问了一个恶意站点。如果没有同源策略,恶意站点的脚本就能发起请求到银行网站并读取你的账户信息(因为浏览器会自动带上 cookie)。

同源策略阻止了这种读取。

限制的边界

注意:同源策略限制的是读取响应,不总是限制发送请求。

  • <img>、<script>、<link> 标签可以加载跨域资源(这是 CDN 能工作的基础);
  • 表单可以提交到跨域地址;
  • 但脚本读取跨域 AJAX 的响应内容会被拦截。

这个看似"不一致"的设计,是历史演进和安全权衡的结果。

CORS

CORS(Cross-Origin Resource Sharing,跨域资源共享)是官方的跨域方案:由服务器声明允许哪些源访问。

简单请求

满足特定条件时(如 GET/POST + 特定的 Content-Type),浏览器直接发请求,并在请求头加上:

Origin: https://your-site.com

服务器如果允许,在响应头返回:

Access-Control-Allow-Origin: https://your-site.com

浏览器看到这个头,就把响应交给 JS。否则拦截。

预检请求(Preflight)

对于"非简单请求"(如 PUT/DELETE、自定义请求头、application/json),浏览器会先发一个 OPTIONS 预检请求:

OPTIONS /api/data
Origin: https://your-site.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type

服务器回应允许的源、方法和头:

Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: content-type
Access-Control-Max-Age: 86400

预检通过后,浏览器才发送真正的请求。

Access-Control-Max-Age 让浏览器缓存预检结果,避免每个请求都 OPTIONS 一次。

携带凭证

默认跨域请求不带 cookie。如果需要带上:

  • 客户端:fetch(url, { credentials: 'include' });
  • 服务器:Access-Control-Allow-Credentials: true。

关键限制:当允许凭证时,Access-Control-Allow-Origin 不能是 *,必须是具体的源。

常见错误

一、服务器没配 CORS 头 → 浏览器报 "blocked by CORS policy"。

解决:在服务器端添加相应的响应头(不是前端能解决的——这是服务器端配置)。

二、带 cookie 时用了 * → 报错。

三、代理层的响应头覆盖 → Nginx 等反向代理可能覆盖应用设置的 CORS 头。

其它跨域方案

一、反向代理(开发环境最常用)

让前端开发服务器代理 API 请求,浏览器看到的是同源。

二、JSONP(已过时)

利用 <script> 标签不受同源限制的特性。只支持 GET,且安全性差。现代开发不应再使用。

三、postMessage

用于窗口/iframe 之间的跨域通信。

说明:本文为浏览器安全的知识整理。