预检请求(options)失败是cors跨域中最隐蔽的问题,表现为浏览器静默卡在许可审查阶段;需确认是否触发预检、检查options请求的请求头与响应头是否匹配、排查中间层是否过滤头字段,并注意浏览器差异。

预检请求(OPTIONS)失败是 CORS 跨域阻断中最隐蔽也最常被误判的一类问题。它不报“缺少 Access-Control-Allow-Origin”,也不显示网络超时,而是让主请求根本发不出去——你点按钮、调接口,控制台静悄悄,Network 面板里只看到一个灰色的 OPTIONS 请求状态为 403、404 或直接 (failed)。这说明浏览器在真正发 POST/PUT/带自定义头的请求前,就被卡在了“许可审查”这一步。
先确认是不是真触发了预检
不是所有跨域请求都会走 OPTIONS。只有满足以下任一条件,浏览器才自动发起预检:
- 请求方法不是 GET、HEAD 或 POST
- POST 请求的 Content-Type 不是以下三者之一:
application/x-www-form-urlencoded、multipart/form-data、text/plain - 设置了自定义请求头(如
X-Request-ID、Authorization、X-Token等)
如果你的请求带了 headers: { 'X-Trace': 'abc' },哪怕只是开发环境加了个日志头,就已经属于“复杂请求”,必须过预检关。
看 Network 面板里的 OPTIONS 请求细节
打开开发者工具 → Network → 找到那个灰色的 OPTIONS 请求,点开看三块内容:
-
Headers 标签页:检查 Request Headers 里是否有
Access-Control-Request-Method(比如PUT)和Access-Control-Request-Headers(比如x-request-id, content-type)——这是浏览器在问:“我打算用这个方法、带这些头,你允不允许?” - Response 标签页:重点看响应状态码。如果是 404,说明后端没暴露 OPTIONS 接口;如果是 403,常见于网关鉴权拦截了 OPTIONS;如果是 200/204 但响应头缺失,则往下查头字段
-
Response Headers:必须包含三项且值匹配:
-
Access-Control-Allow-Methods: PUT, POST, GET(要覆盖你实际要用的方法) -
Access-Control-Allow-Headers: x-request-id, content-type(要精确包含你请求中带的所有自定义头,大小写不敏感但拼写必须一致) -
Access-Control-Allow-Origin: https://your-frontend.com(不能是*,如果主请求带 credentials)
-
排查中间层是否悄悄吃掉了响应头
很多团队用 Nginx、API 网关或 CDN 做反向代理,它们可能默认清除或覆盖 CORS 头。典型表现是:后端代码明明写了头,但浏览器收不到。
- Nginx 配置中需显式开启
add_header Access-Control-Allow-Origin $http_origin;,并确保add_header没被expires或其他指令覆盖 - 阿里云 API 网关、腾讯云 TSF 等平台,需单独开启“CORS 支持”开关,并手动填写允许的方法和头,不能只依赖后端返回
- Firebase Hosting、Vercel Edge Functions 等静态托管服务,对 OPTIONS 请求默认无响应,必须显式配置路由或改用函数代理
火狐与 Chrome 的差异提示要分清
Chrome 控制台常明确提示缺哪个头,而火狐只报 “CORS request did not succeed”。这时别猜,直接按顺序检查:
- OPTIONS 请求有没有发出?没发出 → 检查前端是否用了
file://协议、混用了localhost和127.0.0.1、或 HTTPS 页面请求 HTTP 接口(混合内容拦截) - 发出了但状态为空或 (failed) → 查 DNS、端口连通性、SSL 证书是否有效(尤其自签名证书在火狐里更严格)
- 发出了且有响应但报错 → 回到上一步,逐项核对三个
Access-Control-Allow-*头是否完整、拼写是否正确、值是否匹配
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











