nginx处理options预检请求需用独立location拦截并return 204,所有cors头必须加always参数;origin应动态白名单校验,禁用*;避免if+proxy_pass混用,防止头丢失和502错误。

在 nginx.conf 中正确处理 OPTIONS 预检请求,核心是让 Nginx 自身快速响应,不转发给后端,并确保所有必需的 CORS 响应头真实送达浏览器。关键不在“加头”,而在“头不丢”、路径不冲突、Origin 不滥放。
必须为 add_header 加 always 参数
Nginx 默认只在有响应体且状态码为 2xx/3xx 的情况下注入 add_header。而 return 204 是无体响应,常规 header 会被跳过——浏览器收不到任何 CORS 头,预检直接失败。
所有跨域相关头都需显式加 always:
- add_header 'Access-Control-Allow-Origin' '$http_origin' always;(推荐动态回传,不用 *)
- add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
- add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
- add_header 'Access-Control-Max-Age' '86400' always;(24 小时缓存预检结果)
- add_header 'Access-Control-Allow-Credentials' 'true' always;(若前端带 credentials)
用独立 location 拦截 OPTIONS,禁止 if + proxy_pass 混用
把 if($request_method = 'OPTIONS') 和 proxy_pass 放在同一 location 内,会触发 Nginx 非标准上下文,导致 Host、X-Real-IP 等头丢失,常引发 502 错误。
正确做法是拆成两个 location,且 OPTIONS 块必须前置:
- 先定义专用于预检的 location(路径需能精确匹配 API 前缀,如 location ~ ^/api/)
- 内部用 if 判断并 return 204,同时集中写全 CORS 头
- 再定义正常代理的 location(如 location /api/),只负责 proxy_pass
Origin 白名单要动态校验,别硬写 *
若前端携带 cookie(credentials: include),Access-Control-Allow-Origin 不能为 *,否则浏览器拒绝响应。
生产环境建议用 map 构建安全白名单:
- 在 http 块中定义:map $http_origin $cors_origin { default ""; ~*^https?://(localhost|admin\.example\.com|app\.mycompany\.io)(:[0-9]+)?$ $http_origin; }
- 在预检 location 中使用:add_header 'Access-Control-Allow-Origin' $cors_origin always;
- 不匹配时 $cors_origin 为空,Nginx 不发送该头,浏览器自然拦截——比返回 * 更安全
加缓存头减少重复预检压力
浏览器虽按 Access-Control-Max-Age 缓存预检结果,但 CDN 或中间代理可能忽略。可叠加标准缓存控制:
- add_header 'Cache-Control' 'public, max-age=86400' always;
- expires 86400s;(更可靠,自动设 Expires 和 Cache-Control)
- 确保该 location 中未启用 proxy_cache_bypass 或 proxy_no_cache,避免缓存被绕过











