最稳妥做法是在匹配请求的 location 块内用 if ($request_method = 'OPTIONS') { return 204; } 拦截预检请求,并配合 add_header ... always 设置 CORS 头,确保不代理、不依赖上游、全覆盖各路径。

直接在处理接口的 location 块里写 if ($request_method = 'OPTIONS') 并返回 204,同时用 add_header ... always 补齐 CORS 头,是最稳妥的做法。关键不是“加头”,而是让 OPTIONS 请求不进后端、不触发代理、不依赖上游响应。
必须把配置写进真正匹配请求的 location 块
跨域头不会继承,server 级或父 location 的 add_header 对子 location 里的 proxy_pass 或静态资源完全无效。比如接口走 location /api/,那就得在这个块里单独配:
- 不能只在
server块里写一次,指望它覆盖所有路径 - 静态资源(如
location ~ \.(js|css|png)$)也要单独加,否则前端加载 JS 时可能被拦截 - 宝塔等可视化面板的“添加响应头”功能默认只作用于
location /,API 路径需手动编辑配置文件
OPTIONS 请求要快速响应,不走后端
浏览器对非简单请求(如带 Authorization、Content-Type: application/json、或使用 PUT/DELETE)会先发 OPTIONS 预检。这个请求根本不会到你的 PHP、Node 或 Java 后端,Nginx 必须自己拦住并响应:
- 用
if ($request_method = 'OPTIONS') { return 204; },语义清晰、无响应体、HTTP 兼容性好 - 不要在 if 块里写
proxy_pass或复杂逻辑,避免性能损耗和配置混乱 - 确保
return 204前已用add_header ... always设置好全部 CORS 头
add_header 必须加 always 参数
add_header 默认只在 2xx 和 3xx 响应中生效。而 return 204 是 2xx,看似够用,但若后续加了其他状态码分支(如 405),就容易漏头。加 always 是防错关键:
add_header 'Access-Control-Allow-Origin' '*' always;add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,Content-Type,Authorization' always;- 如果需要暴露自定义响应头,补上
Access-Control-Expose-Headers
区分简单请求和预检请求,按需优化
不是所有跨域都要 OPTIONS。GET/POST + 标准头(如 Content-Type: application/x-www-form-urlencoded)属于简单请求,无需预检。可据此分层配置:
- 对纯前端页面或静态资源,用宽松策略:
add_header ... always即可,不用 if 判断 - 对
/api/这类真实接口路径,才启用完整预检逻辑 - 若需支持凭证(
withCredentials: true),Access-Control-Allow-Origin不能用*,必须写具体域名,并加Access-Control-Allow-Credentials "true" always


















