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

React请求Nginx反向代理的/product服务时出现CORS重定向错误

解决React调用Nginx代理接口的CORS拦截问题

问题现象

Postman请求http://localhost/product服务正常,但React前端调用时触发CORS拦截,错误信息如下:

Access to XMLHttpRequest at 'http://localhost/product' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

用户Nginx配置如下:

worker_processes 4;

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        charset utf-8;
        location / {
            # auth_request     /auth;
            proxy_pass http://user_services:8001;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }

        location /product/ {
            auth_request     /auth;
            # auth_request_set $auth_status $upstream_status;
            rewrite ^/product/(.*) /$1 break;
           
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            add_header 'Access-Control-Allow-Origin' '*' always;
            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
                add_header 'Access-Control-Allow-Headers' 'Authorization, 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;
            }
            proxy_pass http://onboarding_project:8002;
        
        }

        location /notification/ {
            # auth_request     /auth;
            # auth_request_set $auth_status $upstream_status;
            rewrite ^/notification/(.*) /$1 break;
            proxy_pass http://notification_service:8006;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }

        location /payment/ {
        #    auth_request     /auth;
            rewrite ^/payment/(.*)$ /$1 break;
            proxy_pass http://payment_microservices:8003;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' 'http://localhost:3000';
                add_header 'Access-Control-Allow-Methods' 'POST, OPTIONS';  # Adjust this based on your allowed methods
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';  # Adjust the headers as needed
                add_header 'Access-Control-Allow-Credentials' 'true';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain; charset=utf-8';
                add_header 'Content-Length' 0;
                return 204;
            }
            add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
            add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range' always;

        }

        location = /auth {
            internal;
            proxy_pass              http://user_services:8001/api/user/auth;
            add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
            add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range' always;
        
        }
    }
}

React客户端请求代码:

const response = await axios.get("http://localhost/product", {
        headers: {
          Authorization: "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTU2NzYzODEsImV4cCI6MTY5NTY3OTk4MSwiYXVkIjoiNjUxMWY2YWRkN2Q4YmMyZDIyYTE0MWY2IiwiaXNzIjoiaW50ZWxsaWJsb2NrLmNvbSJ9.bJ5Qd6d9GvGnqJu5UFT8m4UTvZsEmly13QqVbMBwFz0"
        }
      });
      console.log(response, "okayyy");
    } catch (e) {
      console.log(e);
    }

问题根源

  1. 路径重定向问题:React请求的是http://localhost/product(不带末尾斜杠),而Nginx配置的location是/product/(带斜杠),Nginx会自动重定向到带斜杠的路径,但浏览器的CORS预请求(OPTIONS)不允许重定向,直接触发拦截。
  2. CORS头不兼容:请求携带Authorization头,此时Access-Control-Allow-Origin不能设为*,必须指定具体源(如http://localhost:3000),否则浏览器会拒绝响应。
  3. OPTIONS请求未跳过auth校验:预请求不需要执行auth_request校验,否则/auth接口的响应可能干扰CORS处理。

解决方案

1. 修改location匹配规则,避免重定向

将location /product/改为location ^~ /product,该规则会匹配所有以/product开头的请求(无论是否带末尾斜杠),避免Nginx自动重定向。

2. 调整OPTIONS请求逻辑,跳过auth校验

在/product的location中,先判断请求方法为OPTIONS时,直接返回CORS头并结束请求,不执行auth_request和后续代理逻辑。

3. 统一CORS头配置

将/product的CORS头与payment模块对齐,指定具体Origin,确保携带Authorization头时的兼容性。

修改后的/product配置片段

location ^~ /product {
    # 对OPTIONS请求直接返回CORS头,跳过auth和代理
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' 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;
    }

    # 非OPTIONS请求执行auth校验
    auth_request     /auth;
    rewrite ^/product/(.*) /$1 break;
    rewrite ^/product$ / break; # 处理不带斜杠的根路径请求
    
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    
    # 非OPTIONS请求返回CORS头
    add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
    add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range' always;
    
    proxy_pass http://onboarding_project:8002;
}

验证步骤

  1. 保存修改后的Nginx配置,执行nginx -s reload重启服务。
  2. 重新发起React客户端请求,检查是否仍有CORS错误。

内容的提问来源于stack exchange,提问作者Balajee Mishra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:51:10