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

生产环境CORS报错:Access-Control-Allow-Origin被误判为通配符

生产环境CORS报错问题排查与解决

我开发了公司网站的登录系统,登录成功后会把refresh token以Cookie形式返回。本地localhost环境运行正常,但生产环境触发CORS报错:

Access to fetch at 'https://my-linux-api.azurewebsites.net/my-site/login' from origin 'https://www.my-site.com' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.

我已经在Node.js接口里明确设置Access-Control-Allow-Origin为https://www.my-site.com,也试过用cors包配置,但还是出现上述错误。本地设置http://localhost:3000时没问题。

相关代码

后端路由代码

router.post("/my-site/login", function(req, res) {    
    var email = req.body.Email;
    var password = req.body.Password;
    var connection = new Connection(config);    

    var query = "SELECT * FROM dbo.Users WHERE Email = @email"
    const request = new Request(query,         
        function(err) {            
            connection.close();
            if (err) {
                console.log(err);
                res.send([]);
            }  
        }
    );
    
    request.addParameter('email', TYPES.VarChar, email);

    var result = [];
    connection.connect((err) => {
        if (err) {
            console.log(err);
            res.send(err);
        }
        else
        {            
            request.on('row', function(columns) {                     
                var col = [] 
                columns.forEach((column) => {                       
                    col.push(column.value);
                });                
                result.push(col);                                
            });

            request.on('doneProc', function(rowCount) {                
                if (result.length > 0) {        
                    
                    if (result[0][4] == password) {
                        if (result[0][8] !== 0) {
                            result[0].splice(4, 1);                            
                            const accessPayload = ({ User: result[0], Type: "Authentication" })
                            const accessToken = jwt.sign(accessPayload, process.env.secretKey, { expiresIn: 86400 });

                            const refreshPayload = ({ User: result[0][0], Type: "Refresh" })
                            const refreshToken = jwt.sign(refreshPayload, process.env.secretKey, { expiresIn: 172800 });
                            
                            res.header('Access-Control-Allow-Origin', "https://www.my-site.com");
                            res.header('Access-Control-Allow-Credentials', true);
                            res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
                            res.setHeader('Authorization', `Bearer ${accessToken}`);    
                            res.cookie('refreshToken', refreshToken, {
                                httpOnly: true,
                                secure: true,
                                sameSite: 'Lax',
                                path: "/"                                
                            })                        
                            res.setHeader('Content-Type', 'application/json');
                            res.json({ success: true, message: "Authorization Successful" });                           
                        }
                        else {                            
                            res.sendStatus(403)
                        }
                    }
                    else {
                        res.sendStatus(401);
                    }

                }
                else {
                    res.sendStatus(401)
                }
            })

            request.on('requestCompleted', (rowCount, more)=> {                    
                connection.close();
            })

            connection.execSql(request);  
        }
    })
});

CORS配置代码

const cors = require('cors');

const corsOptions = {
    origin: 'https://www.my-site.com',
    credentials: true,
};

router.post("/my-site/login", cors(corsOptions), function(req, res) { ....

前端请求代码

export const Login = (email: string, password: string, context: any) =>
  new Promise(async (resolve, reject) => {
    var formData = new FormData();

    formData.append('Email', email);
    formData.append('Password', password);

    await fetch(url + "my-site/login", {
      method: 'POST',
      credentials: 'include',
      body: formData
    })
    .then((res: any) => {       
      let rawToken = res.headers.get("authorization").split(" ")[1];      
      var accessToken = jwt.decode(rawToken);     
      context.setUser(accessToken.User);
      context.setIsAuthenticated(true);
      localStorage.setItem(
        "authorization",
        rawToken
      )
      if (localStorage.getItem("theme") == null) {
        localStorage.setItem("theme", "light");
      }

      resolve("Success")
    })
    .catch((err) => {
      console.log(err);
      if (err.response.status == 403) {
        reject(403);
      } else if (err.response.status == 401) {
        reject(401);
      } else {
        reject("Error, please try again later");
      }
    })
  });

问题根源分析与解决步骤

1. 清理重复的CORS头配置

你当前同时通过cors包和手动res.header()设置CORS头,这会导致响应头冲突,甚至覆盖正确配置。必须二选一:

  • 删除路由代码中所有手动设置的CORS头(res.header('Access-Control-Allow-Origin', ...)等行)
  • 优先使用cors包做全局配置,避免仅在单个路由挂载:
// 在app.js/主入口文件中全局挂载cors中间件
const cors = require('cors');
const corsOptions = {
  origin: 'https://www.my-site.com',
  credentials: true,
};
app.use(cors(corsOptions));

2. 确保OPTIONS预检请求被正确处理

浏览器发送带credentials: 'include'的跨域请求前,会先发送OPTIONS预检请求。如果服务器未正确响应OPTIONS请求,会返回默认的* origin头,触发报错:

  • 用cors包全局配置时,它会自动处理OPTIONS请求,无需额外配置
  • 如果必须单独给路由配置,需添加OPTIONS方法支持:
router.options("/my-site/login", cors(corsOptions));
router.post("/my-site/login", cors(corsOptions), function(req, res) { ... });

3. 调整Cookie的跨域配置

你的API域名my-linux-api.azurewebsites.net和前端域名www.my-site.com属于完全跨域,原Cookie配置的sameSite: 'Lax'无法满足跨域携带要求:

  • 将Cookie的sameSite设为None,同时保留secure: true(仅HTTPS环境生效,符合生产环境要求):
res.cookie('refreshToken', refreshToken, {
  httpOnly: true,
  secure: true,
  sameSite: 'None',
  path: "/"
})

4. 验证实际响应头

用浏览器开发者工具的Network面板,查看登录请求的响应头:

  • 确认Access-Control-Allow-Origin是精确的https://www.my-site.com,而非*
  • 确认Access-Control-Allow-Credentials为true
  • 检查是否存在重复的CORS头,如有则清理冲突配置

5. 前端额外验证

  • 确保fetch的credentials: 'include'配置正确(当前代码已满足)
  • 确认后端已配置FormData解析中间件(如multer),否则req.body会为空,可能引发后续业务错误

内容的提问来源于stack exchange,提问作者Rusty Shackleford

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 23:19:50