预检请求触发条件是请求方法非get/head/post、含非安全头(如authorization)、content-type非三类白名单值;带凭据时需前后端协同配置credentials: 'include'、access-control-allow-credentials: true及精确origin。

掌握 JS 中跨域与 CORS 的高级进阶技巧,关键不在“多学几个配置”,而在于分清场景、理解浏览器真实行为、并能精准干预请求生命周期的每个环节。下面从四个实战维度展开,全是项目中高频踩坑后沉淀下来的硬核要点。
搞懂预检请求(OPTIONS)触发的真实条件
很多人以为“只要用了 POST 就要预检”,其实不然。是否触发 OPTIONS,取决于三个条件是否**同时满足**:
- 请求方法不是 GET/HEAD/POST 三者之一(如 PUT、DELETE、PATCH)
- 请求头包含非“安全头”(如 Authorization、X-Request-ID、Content-Type: application/json)
- Content-Type 不是以下三类之一:application/x-www-form-urlencoded、multipart/form-data、text/plain
例如:fetch('/api/user', { method: 'POST', headers: { 'Content-Type': 'application/json' } }) —— 看似简单,但因 Content-Type 非白名单值,**必然触发预检**。很多后端只配了主路由的 CORS 头,却忘了给 app.options('/api/user') 单独返回正确响应,导致请求卡死在 OPTIONS 阶段。
带凭据(credentials)时的双端协同规则
当需要传递 Cookie 或 HTTP 认证信息时,前后端必须严格配合,漏一环就失败:
-
前端 fetch 必须显式设置:
credentials: 'include'(不能用'same-origin'或省略) -
后端响应头必须包含:
Access-Control-Allow-Credentials: true -
此时 Access-Control-Allow-Origin 不能为 *,必须指定确切源(如
https://myapp.com) -
Cookie 自身也要合规:服务端 Set-Cookie 需含
SameSite=None; Secure(HTTPS 环境下),否则浏览器拒绝发送
常见错误:后端写了 Allow-Credentials: true,但 Origin 还是设成 *,结果浏览器直接拒收整个响应。
开发环境代理 ≠ 生产跨域方案,别混淆角色
Webpack/Vite 的 devServer.proxy 或 Nginx 反向代理,只是**开发阶段绕过浏览器同源检查的临时手段**,本质是让前端请求先打到本地服务器,再由它转发——对浏览器来说,这根本不是跨域请求。
- 它解决的是 本地调试时的便利性问题,不解决任何生产环境的跨域逻辑
- 一旦上线,代理失效,所有跨域逻辑必须由后端通过 CORS 响应头承载
- 切忌把 devServer.proxy 配置当成“CORS 已解决”的心理安慰,上线前务必验证真实跨域路径
精准调试:用 curl 模拟浏览器行为定位问题
浏览器控制台报错模糊?Network 面板看不出门道?用 curl 直接复现请求链路:
- 模拟简单跨域请求:
curl -H "Origin: https://myapp.com" -I https://api.example.com/data,看响应头是否有Access-Control-Allow-Origin - 模拟带凭据请求:
curl -H "Origin: https://myapp.com" -H "Cookie: session=abc" -I https://api.example.com/data - 模拟预检请求:
curl -X OPTIONS -H "Origin: https://myapp.com" -H "Access-Control-Request-Method: PUT" -H "Access-Control-Request-Headers: Content-Type,Authorization" -I https://api.example.com/data
对比响应头与浏览器实际发出的请求头,能快速判断是后端漏配、Nginx 拦截、还是网关层覆盖了 CORS 头。











