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

Express与Nginx已配置CORS 301重定向仍被跨域拦截如何解决

问题根因
  • 跨域fetch默认会自动跟随服务端返回的3xx重定向,整个跳转过程由浏览器自动执行,不会触发JS层的跳转逻辑;且浏览器会对重定向后的目标地址独立做CORS校验。从报错信息看,当前访问的页面源是http://www.example.com,但重定向目标是http://example.com,二者属于不同源,只要重定向后的响应没带对应源的CORS头,就会被拦截。
  • Nginx配置存在逻辑漏洞:Nginx的add_header指令遵循块继承规则——如果当前层级写了add_header,就不会继承外层的同名配置。单独在OPTIONS请求的if块里写了一套CORS头,和外层规则不统一,且外层的Access-Control-Allow-Origin直接取$http_origin没有做白名单校验,遇到重定向后请求头丢失、origin为空的场景,就不会返回正确的CORS头。另外301是永久重定向,浏览器会本地缓存重定向结果,甚至直接把POST请求转为GET请求,进一步加剧头丢失的问题。
  • 前端代码存在低级错误:fetch返回的Response实例是前端响应对象,根本没有redirect()方法,写的response.redirect(response.url)执行时会直接抛JS异常,完全达不到手动跳转的效果。
  • 额外严重安全隐患:登录接口的SQL语句直接拼接用户传入的参数,存在SQL注入风险,攻击者可以构造恶意参数拖走整个用户库。
可落地方案

跨域场景下不要靠接口返回3xx重定向做页面跳转,改成接口返回JSON状态、前端收到成功标识后手动跳页,直接绕开重定向链路的CORS校验问题,这是前后端分离架构的标准实践:

  1. 改造后端Express登录接口,移除301重定向逻辑,统一返回JSON结果,同时修复SQL注入问题:
// CORS配置明确指定允许的源,不要直接用默认cors()
app.use(cors({
    origin: ['http://example.com', 'http://www.example.com'],
    credentials: true // 如果需要传Cookie、Session就开这个,同时不能把origin设为*
}));

app.post('/login', function(req, res) {
    // 用参数化查询,禁止直接拼接用户输入到SQL里
    const query = 'select id, password from user where id = ?';
    db.query(query, [req.body.id], (err, result) => {
        if (err) {
            console.log(err);
            return res.status(500).json({code: 500, msg: '服务端异常'});
        }
        if (result.length === 0) {
            return res.json({code: 400, msg: '账号不存在'});
        }
        if (req.body.pw === result[0].password) {
            // 登录成功直接返回成功标识,不要做重定向
            // 要写Session、Set-Cookie直接在这里写就行
            return res.json({code: 200, msg: '登录成功'});
        } else {
            console.log("密码错误");
            return res.json({code: 400, msg: '密码错误'});
        }
    });
});
  1. 改造前端请求逻辑,删除无效的response.redirect调用,收到登录成功的状态码后用window.location手动跳页:
const url = 'http://api.example.com/login';
fetch(url, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    },
    credentials: 'include', // 跨域传Cookie必须加这个配置
    body: JSON.stringify(data),
})
.then(response => response.json())
.then(res => {
    if (res.code === 200) {
        // 登录成功,手动跳转到主页
        window.location.replace('http://example.com');
    } else {
        // 这里处理账号不存在、密码错误的提示逻辑
        console.log(res.msg);
    }
})
.catch(err => {
    console.log('请求异常', err);
});
  1. 修正Nginx的CORS配置,删除OPTIONS块里重复的CORS头配置,加origin白名单校验,避免规则覆盖:
location / {
    # 原有alias等配置保留
    # 先做origin白名单校验,只允许信任的源跨域
    set $cors_origin "";
    if ($http_origin ~* "^http://(www\.)?example.com$") {
        set $cors_origin $http_origin;
    }

    # 所有响应统一加CORS头,加always参数保证错误响应也带头
    add_header 'Access-Control-Allow-Origin' "$cors_origin" always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;

    # OPTIONS预检请求单独处理
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }

    try_files $uri $uri/ $uri/index.html $uri.html =404;
}
  1. 域名统一配置:把example.com和www.example.com做301归一,比如所有www的请求都跳转到不带www的主域名,避免两个域名混用触发不必要的跨域问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 22:36:20