正确处理nginx跨域预检需为所有add_header加always参数,避免return 204时cors头丢失;禁用if+proxy_pass混用,改用前置options location匹配;生产环境须用map动态白名单替代通配符origin。

在 nginx.conf 或站点配置文件中正确处理跨域预检请求(OPTIONS),关键不是加几个响应头,而是确保预检响应**真实携带所有必需的 CORS 头**,且不被 Nginx 的默认行为过滤掉。
必须为所有 add_header 加上 always 参数
Nginx 的 add_header 指令默认只对 2xx/3xx 响应中由 Nginx 自身生成 body 的情况生效。而 return 204 是无 body 的快速终止,常规 header 注入逻辑会被跳过——结果就是浏览器收不到任何 CORS 头,预检直接失败。
-
错误写法:
add_header 'Access-Control-Allow-Origin' '*'; -
正确写法:
add_header 'Access-Control-Allow-Origin' '*' always; - 同理,以下每行都必须带
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;add_header 'Access-Control-Allow-Credentials' 'true' always;
禁止在同一个 location 中混用 if + proxy_pass
这种结构看似简洁,实则触发 Nginx 非标准上下文,会导致 proxy_set_header 失效、后端收不到 Host 或 X-Real-IP,极易引发 502 错误。
-
典型错误:
location /api/ {<br> if ($request_method = 'OPTIONS') {<br> return 204;<br> }<br> proxy_pass http://backend;<br>} -
推荐拆分两个 location,且预检块必须前置:
# 1. 专用于匹配并响应 OPTIONS 预检(必须写在前面)<br>location ~ ^/api/.*$ {<br> if ($request_method = 'OPTIONS') {<br> add_header 'Access-Control-Allow-Origin' 'https://your-frontend.com' always;<br> add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;<br> add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;<br> add_header 'Access-Control-Max-Age' '1728000' always;<br> add_header 'Content-Type' 'text/plain; charset=utf-8' always;<br> add_header 'Content-Length' '0' always;<br> return 204;<br> }<br>}<br><br># 2. 正常代理转发(放在后面)<br>location /api/ {<br> proxy_pass http://backend;<br> add_header 'Access-Control-Allow-Origin' 'https://your-frontend.com' always;<br> add_header 'Access-Control-Allow-Credentials' 'true' always;<br> # 其他通用 CORS 头可在此统一加(但预检专用头如 Methods/Headers 无需重复)<br>}
Origin 必须精确匹配或动态白名单(生产环境)
若启用 Access-Control-Allow-Credentials: true,Access-Control-Allow-Origin 就不能是通配符 *,否则浏览器拒绝响应。
- 开发环境可临时用:
add_header 'Access-Control-Allow-Origin' '*' always; - 生产环境建议用
map模块做可信域名白名单:map $http_origin $cors_origin {<br> default "";<br> "~^https?://(localhost|app\.example\.com|fe\.company\.net)$" $http_origin;<br>}<br><br>server {<br> # ...<br> location /api/ {<br> add_header 'Access-Control-Allow-Origin' $cors_origin always;<br> # 其他头...<br> }<br>}
验证预检响应是否生效
不要仅靠浏览器控制台判断,用 curl 直接测 OPTIONS 请求:
curl -I -X OPTIONS https://your-domain.com/api/users
确认返回中包含全部预期头,例如:
Access-Control-Allow-Origin: https://app.example.comAccess-Control-Allow-Methods: GET, POST, OPTIONSAccess-Control-Allow-Headers: Authorization, Content-Type-
Content-Length: 0(配合return 204)











