在Nginx中如何为指定location覆盖CORS的Access-Control-Allow-Origin
Nginx指定Location覆盖全局CORS配置失效问题
问题场景
在api.hostname.com的Nginx配置里设置了全局CORS规则,想实现两个目标:
- 主站资源仅允许
https://hostname.com访问 /different/path/路径下的资源开放给任意来源(作为公开API)
原配置如下:
add_header "Access-Control-Allow-Origin" "https://hostname.com"; add_header "Access-Control-Allow-Credentials" "true"; location /different/path/ { add_header "Access-Control-Allow-Origin" "*"; }
问题现象
第三方站点https://other.co.uk访问/different/path/data.php时触发CORS报错:
Access to fetch at 'https://api.hostname.com/different/path/data.php' from origin 'https://other.co.uk' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'https://hostname.com' that is not equal to the supplied origin.
同时还有这些细节:
- 从
hostname.com域内访问时,响应头的CORS信息完全正常 - 第三方访问时,预请求的响应头和域内请求一致,但实际请求没有任何CORS相关头
- 删掉server块的全局CORS头后,第三方访问会提示预请求缺少
Access-Control-Allow-Origin头 - 直接在PHP脚本里手动添加CORS头,第三方就能正常访问
解决方法
核心原因
Nginx的add_header有个关键特性:子location如果没定义add_header,会继承父级所有的add_header;但只要子location定义了任意一个add_header,就会完全覆盖父级的所有add_header,不会自动继承没定义的项。另外,浏览器发起CORS请求前会先发OPTIONS预请求,这个请求没处理好也会导致校验失败。
正确配置方案
# 全局CORS规则,适用于非公开路径 add_header "Access-Control-Allow-Origin" "https://hostname.com" always; add_header "Access-Control-Allow-Credentials" "true" always; # 处理全局OPTIONS预请求 if ($request_method = OPTIONS) { add_header "Access-Control-Allow-Origin" "https://hostname.com" always; add_header "Access-Control-Allow-Credentials" "true" always; add_header "Access-Control-Max-Age" 1728000; add_header "Content-Type" "text/plain; charset=utf-8"; add_header "Content-Length" 0; return 204; } location /different/path/ { # 覆盖全局CORS,定义该路径需要的所有头 add_header "Access-Control-Allow-Origin" "*" always; add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS" always; add_header "Access-Control-Allow-Headers" "Content-Type" always; # 单独处理该路径的OPTIONS预请求 if ($request_method = OPTIONS) { add_header "Access-Control-Allow-Origin" "*" always; add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS" always; add_header "Access-Control-Allow-Headers" "Content-Type" always; add_header "Access-Control-Max-Age" 1728000; add_header "Content-Type" "text/plain; charset=utf-8"; add_header "Content-Length" 0; return 204; } # 这里添加该location的其他业务配置,比如fastcgi_pass等 # fastcgi_pass ...; }
关键细节说明
- 必须加
always参数:确保在所有响应状态码(包括4xx、5xx)下都发送CORS头,避免异常场景下的CORS报错。 - 单独处理OPTIONS请求:浏览器的预请求必须返回正确的CORS头,否则后续的实际请求会被直接拦截。
*和Credentials不能共存:当Access-Control-Allow-Origin设为*时,不能同时设置Access-Control-Allow-Credentials为true,这是浏览器的安全限制,所以公开路径要去掉Credentials相关配置。- 子location要定义全CORS头:不要依赖父级继承,直接在子location里写全需要的所有CORS头,避免出现混合头导致的校验失败。
内容的提问来源于stack exchange,提问作者Tony Merryfield
相关产品推荐
相关产品推荐

