本节摘要:浏览器出于同源策略,禁止跨域读取响应,CORS 是服务器发给浏览器的"跨域通行证"。本节讲清同源限制、预检请求(Preflight)与简单请求的分界,以及
Access-Control-Allow-Origin等响应头的谱系,用一个前端跨域调用的完整演练,展示非简单请求(带 Authorization 头、自定义头)为何必须走预检。
阅读完本节,你应当能够:
你的前端在 app.example.com,API 在 api.example.com,两者域名不同,这就是"跨域"。浏览器为安全设了同源策略:网页脚本默认不允许读取另一个源的数据。这不是故意刁难,而是防"一个网页脚本偷偷读取你别的站点数据"的沙箱。
但现代 Web 业务偏偏要跨域——前端是 SPA,后端在另一域或 CDN。解决方式:由服务器在响应里签一张"通行证",告诉浏览器"这些源我允许你读",这就是 CORS(跨域资源共享)。准入规则由服务器通过响应头声明,浏览器负责执行。
CORS 里最抓头的是"预检"。浏览器会把请求分成两类:
Content-Type 非简单类型(如 application/json 之外的)。这类必须先发一次预检。关键点:带 Authorization(Bearer 令牌)的请求,几乎总是非简单请求,必然触发预检。你在认证接口里看到的 OPTIONS 请求,就是浏览器先探路。
服务器用一组成对的响应头声明准入规则:
| 头 | 作用 | 典型值 |
|---|---|---|
| Access-Control-Allow-Origin | 允许哪些源 | api.example.com 或 * |
| Access-Control-Allow-Methods | 允许哪些方法 | GET, POST, PUT, DELETE, OPTIONS |
| Access-Control-Allow-Headers | 允许哪些请求头 | Content-Type, Authorization, X-Custom |
| Access-Control-Allow-Credentials | 是否允许带凭证 | true(配 allow-origin 不能是 *) |
一条 POST /book 的跨域响应,正确配置大致长这样:
Access-Control-Allow-Origin: app.example.com Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Allow-Credentials: true
背景:前端从 app.example.com 调 api.example.com 的 POST /book,带了 Authorization 头,浏览器报"无法读取响应因为缺乏 CORS 头"。
操作与排查:打开 DevTools 看到先有 OPTIONS 预检被拒——服务器对预检没回 Access-Control-Allow- 一族头。
结果与修复:在网关或服务端给预检返回上述谱系头,允许 app.example.com 这个源与所需方法、头。修复后预检通过、真实请求带允许的头发出、响应也能被读取。
解读:能发请求却读不到响应,是 CORS 最典型的表象——问题常不在"请求发没发出",而在"服务器没签这张通行证,浏览器不放行读取"。排查第一站就是响应头里少没少 Allow 一族。
变式:若把 Allow-Origin 写成 * 却又想带凭证,会直接失败——* 与 Allow-Credentials: true 不能共存,必须写明具体源。
⚠️ 常见坑:调试时图省事把
Allow-Origin写成*放行一切,或干脆关掉同源保护,短期内"能用了",却把安全沙箱拆了个洞。通行证要写明允许的源,越权契约别图省事开全场。
💡 关键直觉:CORS 的通行证由服务器签发、浏览器执行。你的排查窗口永远是"响应头缺不缺 Allow 一族",再往前就是"预检有没有过"。
* 不能配 Allow-Credentials true,要写明具体源。六件交付事全部装箱,整个"接口契约打磨车间"的四道工序也在此收口。回看导读那张全册依赖图,你会发现四车间输出的成品——一份可验收、可演进、抗事故的对外契约——已经在手上了。