Vite代理引发无限请求循环:URL查询参数改写需求
解决Vite代理修改URL查询参数导致的无限循环问题
需要将URL http://localhost:5173/externesysteme/google?code=123&scope=456&state=789 转换为 http://localhost:5173/externesysteme/google?codeExt=123&scopeExt=456&stateExt=789。原因是Keycloak要求这三个参数带Ext后缀,但从第三方API获取的参数无后缀,只能通过Vite代理实现修改。
当前配置的问题:在vite.config.js中配置代理后,因重定向到同一路径仅修改参数,导致服务器陷入无限请求循环直至崩溃。
当前Vite代理配置
server: { proxy: { "/externesysteme/google": { target: "http://localhost:5173", changeOrigin: true, configure: (proxy, options) => { proxy.on("proxyReq", (proxyReq, req, res) => { let url = new URL(req.url, "http://localhost:5173"); const searchParams = url.searchParams; let paramsChanged = false; // 检查并修改查询参数 if (searchParams.has("code")) { const value = searchParams.get("code"); searchParams.delete("code"); searchParams.append("codeExt", value); paramsChanged = true; } if (searchParams.has("scope")) { const value = searchParams.get("scope"); searchParams.delete("scope"); searchParams.append("scopeExt", value); paramsChanged = true; } if (searchParams.has("state")) { const value = searchParams.get("state"); searchParams.delete("state"); searchParams.append("stateExt", value); paramsChanged = true; } if (paramsChanged) { const newLocation = url.pathname + "?" + searchParams.toString(); res.writeHead(301, { Location: newLocation, "x-rewritten-url": "true", }); console.log("Redirecting to:", newLocation); res.end(); } }); }, }, }, },
此前正常运行的NGINX配置
location /externesysteme/datev { if ($args ~ ^(.+&|)code(=.*)$) { return 301 "$uri?$1codeExt$2"; } if ($args ~ ^(.+&|)state(=.*)$) { return 301 "$uri?$1stateExt$2"; } if ($args ~ ^(.+&|)scope(=.*)$) { return 301 "$uri?$1scopeExt$2"; } }
问题根源与解决方案
问题原因
重定向后的新请求依然匹配/externesysteme/google代理规则,导致代理反复触发参数修改逻辑,形成无限循环。NGINX的重定向不会重复触发同一路径的规则判断,但Vite代理会拦截所有匹配路径的请求,包括重定向后的请求。
方案一:直接修改请求URL(推荐)
使用Vite代理的rewrite钩子直接修改请求路径,无需浏览器重定向,从根源避免循环问题。
修改后的Vite配置:
server: { proxy: { "/externesysteme/google": { target: "http://localhost:5173", changeOrigin: true, rewrite: (path) => { const [basePath, queryString] = path.split('?'); if (!queryString) return path; const searchParams = new URLSearchParams(queryString); let paramsChanged = false; // 修改参数:替换为带Ext后缀的键名 if (searchParams.has('code')) { searchParams.set('codeExt', searchParams.get('code')); searchParams.delete('code'); paramsChanged = true; } if (searchParams.has('scope')) { searchParams.set('scopeExt', searchParams.get('scope')); searchParams.delete('scope'); paramsChanged = true; } if (searchParams.has('state')) { searchParams.set('stateExt', searchParams.get('state')); searchParams.delete('state'); paramsChanged = true; } return paramsChanged ? `${basePath}?${searchParams.toString()}` : path; }, }, }, },
方案二:通过自定义头跳过重复处理
如果必须使用重定向逻辑,可通过检查自定义请求头判断是否已处理过该请求,避免重复触发修改。
修改后的Vite配置:
server: { proxy: { "/externesysteme/google": { target: "http://localhost:5173", changeOrigin: true, configure: (proxy, options) => { proxy.on("proxyReq", (proxyReq, req, res) => { // 若请求已标记为处理过,直接跳过 if (req.headers['x-rewritten-url'] === 'true') { return; } let url = new URL(req.url, "http://localhost:5173"); const searchParams = url.searchParams; let paramsChanged = false; // 检查并修改查询参数 if (searchParams.has("code")) { const value = searchParams.get("code"); searchParams.delete("code"); searchParams.append("codeExt", value); paramsChanged = true; } if (searchParams.has("scope")) { const value = searchParams.get("scope"); searchParams.delete("scope"); searchParams.append("scopeExt", value); paramsChanged = true; } if (searchParams.has("state")) { const value = searchParams.get("state"); searchParams.delete("state"); searchParams.append("stateExt", value); paramsChanged = true; } if (paramsChanged) { const newLocation = url.pathname + "?" + searchParams.toString(); res.writeHead(302, { // 使用302临时重定向更适配场景 Location: newLocation, "x-rewritten-url": "true", }); console.log("Redirecting to:", newLocation); res.end(); } }); }, }, }, },
方案说明
- 方案一无需浏览器重定向,性能更优且逻辑简洁,是首选方案。
- 方案二依赖自定义请求头标记处理状态,需确保浏览器重定向时携带该头,兼容性略逊于方案一。
内容的提问来源于stack exchange,提问作者Maximilian Grinik
相关产品推荐
相关产品推荐

