OPTIONS预检失败主因是Nginx未显式处理该方法且未返回合规CORS响应头;须在location中配置add_header加always参数,并用if拦截OPTIONS返回204,同时确保Origin校验与响应头精确匹配。

OPTIONS预检请求失败,通常不是Nginx没转发,而是它被“悄悄拦下”或“没正确响应”,导致浏览器终止后续真实请求。核心问题在于:Nginx默认不处理OPTIONS方法,也不自动返回CORS所需头信息,必须显式配置。
确认预检请求是否真的到达Nginx
浏览器发起跨域请求前,会先发一个无body、仅含Origin和Access-Control-Request-*头的OPTIONS请求。如果这个请求根本没进Nginx,就不是配置问题,而是前端发错地址、DNS解析错误、或中间有CDN/防火墙拦截。
排查方法:
- 在Nginx access_log中加 log_format 记录 $request_method $request_uri $status,然后复现操作,看日志里有没有 OPTIONS 行
- 用 curl 模拟预检:curl -X OPTIONS -H "Origin: https://example.com" -H "Access-Control-Request-Method: POST" -I http://your-api.com/path
- 若返回 405 Method Not Allowed 或直接超时,说明请求卡在Nginx层;若返回 200 但缺CORS头,说明到了Nginx但响应不合规
确保Nginx显式允许并响应OPTIONS请求
Nginx默认不处理OPTIONS(尤其当location中只配了proxy_pass且没匹配到对应method时),需主动捕获并返回204或200。不能依赖后端处理——预检必须在Nginx层面快速响应,否则延迟或失败。
推荐写法(放在对应 location 块内):
- add_header Access-Control-Allow-Origin "$http_origin" always;(always确保OPTIONS也生效)
- add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE" always;
- add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" always;
- add_header Access-Control-Expose-Headers "Content-Length,Content-Range" always;
- if ($request_method = 'OPTIONS') { return 204; }(最轻量,无body;也可用200+空body)
注意变量作用域与always参数
add_header 在 if 块或非server/location顶层作用域中默认不继承,容易漏掉。常见坑:
- 把 add_header 写在 if ($request_method = 'OPTIONS') { } 外面,但没加 always,则OPTIONS响应里不会带这些头
- 多个location嵌套时,子location未重复声明 add_header,导致预检响应缺失关键头
- $http_origin 要配合 valid_referers 或白名单校验(如 map $http_origin $cors_origin),避免通配符 * 与 credentials 冲突
验证最终响应是否符合浏览器要求
成功响应的OPTIONS请求必须同时满足:
- 状态码是 200 或 204(不能是301/404/502)
- 包含 Access-Control-Allow-Origin(值需精确匹配请求Origin,不能为*当带credentials时)
- 包含 Access-Control-Allow-Methods(列出实际允许的方法,至少含当前请求的Method)
- 包含 Access-Control-Allow-Headers(覆盖请求中的 Access-Control-Request-Headers)
- 所有CORS头都需带 always 标志,否则Nginx在return/rewrite等流程中可能丢弃

















