使用fetch()调用含res.redirect()的接口时出现CORS错误及跳转问题
使用fetch()调用含res.redirect()的接口时遭遇CORS错误
当用fetch()调用后端带res.redirect()的接口时,一直触发CORS错误,但直接在浏览器地址栏访问该接口、或者用<a>标签跳转时,重定向完全正常。我知道这种逻辑是可行的,比如keycloak-connect这类npm包就实现了相同的重定向逻辑且运行正常,只是自己搞不定。
后端代码
const corsOptions = { origin: function (origin, callback) { callback(null, true); }, }; /** 返回前端HTML页面 **/ router.get("/", (req, res) => { res.sendFile(path.join(__dirname, "view", "index.html")); }); /** 重定向接口 **/ // 尝试过修改/移除CORS配置,都没用 router.get("/redirector", cors(corsOptions), (req, res) => { res.redirect("https://google.com"); }); app.use( bodyParser.urlencoded({ extended: true, }), bodyParser.json(), router ); const server = http.createServer(app); server.listen(8080, () => { console.log("http://localhost:8080"); });
前端代码
<body> <h1>Home</h1> <br /> <button id="btn">调用重定向接口</button> <script> const btn = document.getElementById('btn'); btn.addEventListener('click', async () => { try { const response = await fetch('/redirector'); } catch (error) { console.error(error); } }); </script> </body>
运行效果演示

问题原因
fetch()属于AJAX请求,浏览器会严格执行CORS策略。当后端返回3xx重定向时,fetch会自动跟进重定向到目标地址(这里是google.com),但google.com并没有配置允许你的前端域名的CORS规则,所以浏览器拦截了这个跨域请求,抛出CORS错误。而直接地址栏访问或<a>标签跳转属于浏览器顶级导航,不受AJAX的CORS限制。
解决方法
方法1:后端返回地址,前端主动跳转
不让fetch自动跟进重定向,后端返回目标URL,前端拿到后手动跳转:
- 后端修改:
router.get("/redirector", cors(corsOptions), (req, res) => { res.json({ redirectUrl: "https://google.com" }); });
- 前端修改:
btn.addEventListener('click', async () => { try { const response = await fetch('/redirector'); const data = await response.json(); window.location.href = data.redirectUrl; } catch (error) { console.error(error); } });
方法2:用表单提交绕过AJAX限制
创建隐藏表单提交,和<a>标签跳转原理一致,属于浏览器顶级导航,不会触发CORS:
btn.addEventListener('click', () => { const form = document.createElement('form'); form.method = 'GET'; form.action = '/redirector'; document.body.appendChild(form); form.submit(); document.body.removeChild(form); });
方法3:后端代理转发(仅适用于可控目标域名)
如果重定向目标是自己的服务,可以在后端做代理,避免前端跨域:
const axios = require('axios'); // 需先安装axios router.get("/redirector", cors(corsOptions), async (req, res) => { try { const targetRes = await axios.get("https://google.com", { responseType: 'stream' }); res.set(targetRes.headers); targetRes.data.pipe(res); } catch (err) { res.status(500).send(err.message); } });
注意:此方法对google.com这类外部域名可能无效,对方可能有反爬或安全限制。
内容的提问来源于stack exchange,提问作者Kev
相关产品推荐
相关产品推荐

