必须为所有add_header加always参数并用独立location拦截options预检请求,确保204响应携带完整cors头;origin需用map白名单动态控制,且启用underscores_in_headers以保留自定义请求头。

直接在 proxy_pass 所在的 location 块里加 add_header 并不能让 OPTIONS 预检响应携带 CORS 头——因为 Nginx 默认不给 204、304 或错误响应加 header,而预检通常用 return 204 快速响应,此时头信息会被跳过。核心不是“转发时加头”,而是“确保预检响应本身带全 CORS 头”。
必须为所有 add_header 加 always 参数
不加 always,Nginx 只在生成 body 的 2xx/3xx 响应中注入 header;但 return 204 没有 body,常规 header 就丢了。
add_header 'Access-Control-Allow-Origin' 'https://your-frontend.com' 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' '1728000' always;- 若需支持凭证(如 Cookie),再加:
add_header 'Access-Control-Allow-Credentials' 'true' always;(此时 Origin 不能为*)
用独立 location 拦截并响应 OPTIONS,别混用 if + proxy_pass
把预检逻辑和代理逻辑拆开,避免 Nginx 上下文错乱导致 header 丢失或 502 错误。
- 预检 location 必须写在 proxy_pass location 之前
- 用正则匹配路径(如
location ~ ^/api/.*$),确保覆盖所有可能触发预检的接口 - 内部用
if ($request_method = 'OPTIONS')判断,然后return 204,同时带上全部带always的 CORS 头 - 不要在同一个 location 里既写
if ($request_method = 'OPTIONS') { ... }又写proxy_pass
Origin 白名单建议用 map 动态控制
生产环境禁用 Access-Control-Allow-Origin *,尤其开启 Allow-Credentials 时。
- 在
server或http块中定义map:
default "";
"https://a.example.com" "https://a.example.com";
"https://b.example.com" "https://b.example.com";
}
- 然后在预检响应中使用:
add_header 'Access-Control-Allow-Origin' $cors_origin always; - 若 $cors_origin 为空(即来源不在白名单),Nginx 不会发该 header,浏览器自然拒绝请求
确认 proxy_pass 请求头未被意外过滤
某些自定义请求头(如 X-Auth-Token、含下划线的 user_id)可能被 Nginx 默认丢弃。
- 在 server 或 location 块中启用:
underscores_in_headers on; - 显式转发关键头:
proxy_set_header X-Auth-Token $http_x_auth_token; - 确保
proxy_pass_request_headers on;(虽默认开启,显式声明更稳妥)











