You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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 ...;
}

关键细节说明

  1. 必须加always参数:确保在所有响应状态码(包括4xx、5xx)下都发送CORS头,避免异常场景下的CORS报错。
  2. 单独处理OPTIONS请求:浏览器的预请求必须返回正确的CORS头,否则后续的实际请求会被直接拦截。
  3. *和Credentials不能共存:当Access-Control-Allow-Origin设为*时,不能同时设置Access-Control-Allow-Credentials为true,这是浏览器的安全限制,所以公开路径要去掉Credentials相关配置。
  4. 子location要定义全CORS头:不要依赖父级继承,直接在子location里写全需要的所有CORS头,避免出现混合头导致的校验失败。

内容的提问来源于stack exchange,提问作者Tony Merryfield

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:40:09