OPTIONS 预检请求返回 403 导致跨域失败怎么排查后端拦截器配置

文章导读
OPTIONS 预检请求返回 403 通常是因为后端安全框架(如 Spring Security)或网关(如 Nginx)在 CORS 逻辑执行前拦截了请求。最推荐的处理方向是在安全配置中显式放行 OPTIONS 方法,或在网关层直接返回 204 状态码。适用场景为前后端分离架构中涉及自定义请求头或非简单请求的跨域调用,风险边界在于放行 OPTIONS 可能绕过部分鉴权逻辑,需确保仅针对预检请求豁
📋 目录
  1. A 命令速用版
  2. B 为什么会这样
  3. C 分步处理
  4. D 怎么验证是否生效
  5. E 常见坑
  6. F 常见问题
  7. G 参考来源
A A

OPTIONS 预检请求返回 403 通常是因为后端安全框架(如 Spring Security)或网关(如 Nginx)在 CORS 逻辑执行前拦截了请求。最推荐的处理方向是在安全配置中显式放行 OPTIONS 方法,或在网关层直接返回 204 状态码。适用场景为前后端分离架构中涉及自定义请求头或非简单请求的跨域调用,风险边界在于放行 OPTIONS 可能绕过部分鉴权逻辑,需确保仅针对预检请求豁免。

先说结论:OPTIONS 请求被安全链拦截是 403 主因,需在鉴权前放行

  • 先确认 Nginx 是否拦截 OPTIONS
  • 先处理 Spring Security 放行 OPTIONS
  • 再验证响应头是否包含 CORS 字段

命令速用版

使用 curl 模拟浏览器预检请求,快速定位是网关拦截还是应用拦截。

curl -X OPTIONS -H "Origin: https://your-frontend.com" -H "Access-Control-Request-Method: POST" -I http://your-api.com/endpoint

若返回 403 或 405,说明预检请求未通过;若返回 204 且含 Access-Control-Allow-Origin,则配置生效。

为什么会这样

浏览器在发送复杂请求(如带自定义 Header 或 Content-Type 为 application/json)前,会自动发送 OPTIONS 预检请求。后端安全框架(如 Spring Security)默认将 OPTIONS 视为普通请求进行鉴权,若未携带 Token 或未配置放行,直接返回 403 Forbidden,导致浏览器拦截后续真实请求。Nginx 若未配置 OPTIONS 处理,也可能直接拒绝。

分步处理

第一步:检查 Nginx 配置

若架构中有 Nginx,需确保 location 块内显式处理 OPTIONS 请求,避免请求透传到后端被拒。

location /api/ {
    if ($request_method = 'OPTIONS') {
        add_header Access-Control-Allow-Origin "https://your-frontend.com";
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
        add_header Access-Control-Allow-Headers "Content-Type, Authorization";
        return 204;
    }
}

注意 add_header 必须写在 location 块内,server 块配置可能不继承。

第二步:配置 Spring Security 放行

在 Spring Security 配置类中,显式允许 OPTIONS 方法绕过鉴权。

// 方式 1:忽略 OPTIONS 请求
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");

// 方式 2:允许所有 OPTIONS 请求
http.antMatchers(HttpMethod.OPTIONS, "/**").permitAll();

确保配置位于 securityFilterChain 构建之前,避免过滤器链提前拦截。

第三步:检查后端拦截器

OPTIONS 预检请求返回 403 导致跨域失败怎么排查后端拦截器配置

若使用自定义 HandlerInterceptor,需在 preHandle 中判断请求方法,避免业务拦截器拒绝 OPTIONS。

if ("OPTIONS".equals(request.getMethod())) {
    response.setHeader("Access-Control-Allow-Origin", "*");
    return true;
}

怎么验证是否生效

打开浏览器开发者工具 Network 面板,勾选 Preserve log,刷新页面触发请求。查看 OPTIONS 请求状态码是否为 204 或 200,响应头是否包含 Access-Control-Allow-Origin 和 Access-Control-Allow-Headers。若 OPTIONS 成功但 POST 仍失败,检查后端业务逻辑是否返回 403。

常见坑

1. 凭证与通配符冲突

若前端设置 credentials: 'include',后端 Access-Control-Allow-Origin 不能为 *,必须指定具体域名,否则浏览器拒绝携带凭证。

2. Nginx 头继承问题

Nginx 的 add_header 指令在 location 块中不会继承 server 块配置,需在每个需要跨域的 location 中重复添加 CORS 头。

3. 安全模块干扰

部分安全模块(如 mod_security)可能误判 OPTIONS 请求为攻击行为,需检查安全日志确认是否被 WAF 拦截。

常见问题

OPTIONS 返回 403 和 401 有什么区别

401 表示未认证,403 表示已认证但无权访问或安全策略拒绝。OPTIONS 返回 403 通常是安全框架直接拒绝未携带 Token 的预检请求。

为什么 Apifox 测试正常但浏览器报错

Apifox 不执行浏览器 CORS 策略,直接发送请求。浏览器会先发送 OPTIONS 预检,若后端未配置 CORS 头或拦截预检,浏览器直接报错。

配置了@CrossOrigin 为什么还返回 403

Spring Security 过滤器链优先级高于@CrossOrigin 注解,若 Security 未放行 OPTIONS,请求在到达 Controller 前已被拦截。

参考来源

  • Axios 请求返回 403 Forbidden 错误的完整排查与解决方案
  • Nginx 中 API 跨域请求返回 403 Forbidden 原因及修复指南
  • Spring security options 请求返回 403
  • Laravel API 跨域问题怎么解决_Laravel CORS 配置与 Nginx 检查
  • 发送预检请求出现 403,且真实请求出现跨域错误