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

Nginx反向代理下Access-Control-Allow-Origin多值CORS错误修复咨询

问题背景

我的架构配置如下:

  • React应用部署在测试域名amplifyapp.com
  • Keycloak授权服务器
  • 部署在Nginx反向代理后的Spring Gateway

Nginx用作反向代理卸载SSL,配置如下:

server {
    server_name api1.domain.org;
    index index.html index.htm;
    access_log /var/log/nginx/bmiapp.log;
    error_log  /var/log/nginx/bmiapp-error.log error;

    set $cors_credentials '';
set $cors_content_type '';
set $cors_content_length '';

if ($http_origin ~ '.+') {
    set $cors_credentials 'true';
}

if ($request_method = OPTIONS) {
    set $cors_content_type 'text/plain';
    set $cors_content_length '0';
}

# empty header will not be added
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials $cors_credentials always;
add_header Access-Control-Allow-Methods $http_access_control_request_method always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Content-Type $cors_content_type;
add_header Content-Length $cors_content_length;

if ($request_method = OPTIONS) {
    return 204;
}

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://ip:8134;
        proxy_redirect off;
    }


    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/api1.domain.org/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/api1.domain.org/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    server_name api2.domain.org;
    index index.html index.htm;
    access_log /var/log/nginx/bmiapp.log;
    error_log  /var/log/nginx/bmiapp-error.log error;

    set $cors_credentials '';
set $cors_content_type '';
set $cors_content_length '';

if ($http_origin ~ '.+') {
    set $cors_credentials 'true';
}

if ($request_method = OPTIONS) {
    set $cors_content_type 'text/plain';
    set $cors_content_length '0';
}

# empty header will not be added
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials $cors_credentials always;
add_header Access-Control-Allow-Methods $http_access_control_request_method always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Content-Type $cors_content_type;
add_header Content-Length $cors_content_length;

if ($request_method = OPTIONS) {
    return 204;
}


    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://ip:8138;
        proxy_redirect off;
    }


    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/api2.domain.org/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/api2.domain.org/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = api1.domain.org) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    server_name api1.domain.org;
    listen 80;
    return 404; # managed by Certbot


}

server {
    if ($host = api2.domain.org) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    server_name api2.domain.org;
    listen 80;
    return 404; # managed by Certbot


}

调用后端接口时出现以下跨域错误:

Access to XMLHttpRequest at 'https://api2.domain.org/api/microservice/dashboard/test' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:3000, http://localhost:3000', but only one is allowed.
问题原因

错误核心是Nginx和后端Spring Gateway(或下游微服务)同时设置了Access-Control-Allow-Origin响应头,导致浏览器接收到两个相同的Origin值,触发CORS规则拦截。

解决方案

只需选择以下一种方案执行即可,不要同时启用两端的CORS配置。

方案一:保留Nginx的CORS配置,关闭后端CORS设置

  1. 检查Spring Gateway或下游微服务中的CORS配置,包括:
    • 代码中的@CrossOrigin注解
    • 全局CORS配置类(如CorsConfig)
    • application.yml/application.properties中的Spring Gateway CORS配置
      将上述配置全部禁用或删除。
  2. 确保Nginx配置中的always参数保留,该参数能保证错误响应也会带上CORS头,避免跨域问题。

方案二:关闭Nginx的CORS配置,由后端统一处理

  1. 修改Nginx的api1.domain.org和api2.domain.org两个server块,删除以下所有CORS相关代码:
    set $cors_credentials '';
    set $cors_content_type '';
    set $cors_content_length '';
    
    if ($http_origin ~ '.+') {
        set $cors_credentials 'true';
    }
    
    if ($request_method = OPTIONS) {
        set $cors_content_type 'text/plain';
        set $cors_content_length '0';
    }
    
    # empty header will not be added
    add_header Access-Control-Allow-Origin $http_origin always;
    add_header Access-Control-Allow-Credentials $cors_credentials always;
    add_header Access-Control-Allow-Methods $http_access_control_request_method always;
    add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
    add_header Content-Type $cors_content_type;
    add_header Content-Length $cors_content_length;
    
    if ($request_method = OPTIONS) {
        return 204;
    }
    
  2. 在Spring Gateway中配置全局CORS规则,示例如下:

    代码配置方式

    @Configuration
    public class CorsConfig {
        @Bean
        public CorsWebFilter corsWebFilter() {
            CorsConfiguration config = new CorsConfiguration();
            // 允许携带Cookie
            config.setAllowCredentials(true);
            // 指定允许的源,生产环境建议替换为具体域名
            config.addAllowedOrigin("http://localhost:3000");
            config.addAllowedOrigin("https://xxx.amplifyapp.com");
            // 允许所有请求方法
            config.addAllowedMethod("*");
            // 允许所有请求头
            config.addAllowedHeader("*");
    
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", config);
    
            return new CorsWebFilter(source);
        }
    }
    

    YAML配置方式

    spring:
      cloud:
        gateway:
          globalcors:
            cors-configurations:
              '[/**]':
                allowed-origins: 
                  - "http://localhost:3000"
                  - "https://xxx.amplifyapp.com"
                allowed-methods: "*"
                allowed-headers: "*"
                allow-credentials: true
    
注意事项
  • 生产环境禁止使用*作为允许的Origin,必须指定具体的业务域名,避免安全风险。
  • 修改Nginx配置后,执行sudo nginx -s reload命令使配置生效。
  • 测试时建议清空浏览器缓存,避免旧的CORS缓存影响结果。

内容的提问来源于stack exchange,提问作者Peter Penzov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 07:19:55