CORS跨域 Access-Control-Allow-Origin 响应头重复设置报错处理

最近在小程序IDE测试埋点上传时访问负载均衡代理的域名(wx.chegva.com)转发到目标nginx服务时,报错“Access-Control-Allow-Origin cannot contain more than one origin”。在浏览器访问时,跨域报错 https://wx.chegva.com has been blocked by  CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'https://wx.chegva.com, *', but only one is allowed。同时,在目标nginx服务中看不到访问请求。这个错误通常发生在响应头Access-Control-Allow-Origin包含了多个源(origin)的情况下。根据CORS规范,这个响应头只能包含一个源(即一个具体的域名)或者*(表示允许所有源),但不能同时包含多个源(如https://example.com, another.com)或多个通配符(如*, *)

原因分析及排查:

  1. 在某个环节(可能是负载均衡或者nginx配置)中,多次设置了Access-Control-Allow-Origin头,导致合并成了多个值。

  2. 可能由于多次转发,每一层都添加了该头部,导致最终响应头中出现了多个Access-Control-Allow-Origin

  3. 确保请求能够正确转发到目标Nginx服务,以便我们可以调试和查看日志。

  4. 解决Access-Control-Allow-Origin包含多个值的问题。

第一步:确保请求正确转发

  • 检查负载均衡配置:确认负载均衡(可能是Nginx、云服务商的LB等)是否正确将请求转发到了目标nginx服务。

  • 在目标nginx服务上开启详细日志,确保能够记录到请求。

  • 可以在负载均衡层也开启日志,查看请求是否被正确转发。

第二步:解决多个Access-Control-Allow-Origin值的问题

  • 在负载均衡和目标nginx服务中,确保只在某一个地方设置Access-Control-Allow-Origin,避免重复设置。

  • 如果使用了多层代理,每层都可能添加CORS头,那么我们需要在某一层(通常是最后一层)设置,而其他层则不要设置。

具体到配置:

假设我们的架构是:小程序 -> 负载均衡(LB)-> 目标Nginx服务

在 目标 Nginx 服务 或 负载均衡层 统一配置 CORS 规则,避免多级重复设置

方案1:在目标Nginx服务设置CORS头,负载均衡层不设置(推荐)

在目标Nginx服务中设置CORS头,并确保负载均衡层不会添加额外的CORS头。

# 目标 Nginx 配置
location / {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    ... # 其他 CORS 配置
    proxy_pass ...;
}

负载均衡配置
移除所有 CORS 相关头部,仅做纯转发,透传请求头到目标 Nginx 服务

# 负载均衡层(LB)Nginx 配置示例
http {
    # 定义后端服务器组
    upstream backend_servers {
        # 目标Nginx服务的IP和端口(可配置多个)
        server 192.168.1.100:80 weight=5;  # 目标Nginx实例1
        server 192.168.1.101:80 weight=5;  # 目标Nginx实例2
        keepalive 32;  # 保持长连接提升性能
    }

    server {
        listen 80;
        server_name wx.chegva.com;  # 小程序访问域名

        # 关键:透传所有原始请求头到后端
        location / {
            # 核心透传配置(必须包含以下头部)
            proxy_set_header Host $host;           # 传递原始域名
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Origin $http_origin;   # 透传Origin头(CORS必需)
            
            # 移除所有CORS响应头(确保不添加任何Access-Control-*)
            # 注意:这里没有 add_header 指令!

            # 连接参数优化
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_pass http://backend_servers;  # 转发到后端服务器组
        }

        # 可选:负载均衡状态监控页面(内部访问)
        location /nginx_status {
            stub_status on;
            access_log off;
            allow 192.168.0.0/16;  # 仅内网访问
            deny all;
        }
    }
}
方案2:在负载均衡层设置CORS头,目标Nginx服务不设置

在负载均衡的配置中,设置Access-Control-Allow-Origin为单一值(比如小程序要求的域名)或者*(如果允许所有),同时确保目标Nginx服务不设置该头部。

# 负载均衡配置
location / {
    add_header 'Access-Control-Allow-Origin' '*';
    ... # 其他 CORS 配置
    proxy_pass http://目标Nginx;
}

目标 Nginx 配置

移除所有 CORS 头部,避免重复添加。

如何避免多个值?

  • 在Nginx配置中,设置CORS头时,使用add_header指令,但要避免在多个位置重复设置。

# Nginx去除Origin
add_header 'Access-Control-Allow-Origin' '';
  • 同时,检查是否在某个配置中使用了多个add_header指令来设置同一个头部。

方案三:动态传递 Origin(更安全)

如果需支持带凭证的请求(如 Cookies),改用动态来源:

# 目标 Nginx 配置
location / {
    # 动态设置允许来源
    set $cors_origin "";
    if ($http_origin ~* (https?://[^/]*\.chegva\.com$)) {
        set $cors_origin $http_origin;
    }

    add_header 'Access-Control-Allow-Origin' $cors_origin;
    add_header 'Access-Control-Allow-Credentials' 'true'; # 允许凭证
    ...
}

关键点

  • 负载均衡需透传 Origin 头(配置 proxy_set_header Origin $http_origin;

  • 目标 Nginx 需移除通配符 *

验证配置是否正确的步骤:

  1. 检查响应头(使用 curl 测试):

    curl -I -H "Origin: https://wx.chegva.com" https://wx.chegva.com/api
    • ✅ 正确结果:响应头中不会出现 Access-Control-Allow-Origin

    • ❌ 错误结果:如果看到该头部,说明 LB 层仍有 CORS 配置


参考:Nginx通过CORS实现跨域

anzhihe 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:https://chegva.com/6494.html | ☆★★每天进步一点点,加油!★★☆ | 

您可能还感兴趣的文章!

发表评论

电子邮件地址不会被公开。 必填项已用*标注