基于Firebase的Next.js URL规范化中间件部署异常排查
我开发了一个Next.js中间件,用于对GET参数排序并移除多余的&符号(比如将https://lol.kek/?b=1&z=2&a=3&&&转为https://lol.kek/?a=3&b=1&z=2),本地开发和Firebase模拟器都正常,但部署到Firebase Hosting后完全失效——日志显示传入中间件的URL已经被规范化,导致重定向逻辑不触发。
核心原因:Firebase Hosting的自动URL规范化
Firebase Hosting默认会对所有传入的请求URL做标准化处理,其中就包括:
- 自动按字母顺序排序查询参数的键名
- 移除多余的
&分隔符(包括连续的&&&) - 去除参数值前后的无效空格
这个处理是在请求到达你的Next.js中间件/SSR函数之前完成的,所以你的中间件拿到的已经是被Firebase处理后的URL,自然无法触发重定向逻辑。
解决方案
方案1:修改Firebase Hosting配置,保留原始查询字符串
在项目根目录的firebase.json中,修改hosting下的rewrites规则,添加preserveQueryString: true,让Firebase保留原始未处理的查询参数:
{ "hosting": { // 你的其他Hosting配置(比如public目录、headers等) "rewrites": [ { "source": "**", "function": "nextServerDev", "preserveQueryString": true } ] } }
这个配置会告诉Firebase Hosting不修改原始的查询字符串,直接将完整的原始URL传递给你的SSR函数,这样你的Next.js中间件就能拿到未规范化的URL,正常执行重定向逻辑。
方案2:在SSR函数层提前处理原始URL
如果方案1不生效,或者你需要更灵活的控制,可以在Firebase SSR函数中直接获取原始请求URL,提前完成规范化重定向,再传递给Next.js处理:
修改你的SSR函数代码:
const { https } = require('firebase-functions'); const next = require('next'); const config = require('../next-static-config.js'); const normalizeUrl = require('normalize-url'); // 需要先安装这个包 const hostname = 'localhost'; const port = 3000; const baseConfig = { dev: false, hostname, port, conf: { distDir: '.next', ...config }, minimalMode: false }; const server = next({ ...baseConfig }); const nextjsHandle = server.getRequestHandler(); exports.nextServerDev = https.onRequest((req, res) => { // 获取原始未规范化的完整URL const originalPath = req.headers['x-original-url'] || req.url; const fullOriginalUrl = `https://${req.headers.host}${originalPath}`; // 执行URL规范化 const normalizedUrl = normalizeUrl(fullOriginalUrl); // 如果原始URL和规范化后的不一致,直接重定向 if (fullOriginalUrl !== normalizedUrl) { return res.redirect(301, normalizedUrl); } // 否则继续交给Next.js处理 return nextjsHandle(req, res); });
这种方式绕过了Next.js中间件的限制,直接在函数入口处理URL规范化,确保无论Firebase Hosting是否修改URL,都能触发重定向。
验证
部署修改后的配置或代码后,测试带有乱序参数和多余&的URL:
- 访问
https://your-domain/?b=1&z=2&a=3&&& - 检查浏览器地址栏是否自动跳转为
https://your-domain/?a=3&b=1&z=2 - 查看Firebase函数日志,确认原始URL被正确捕获并处理
内容的提问来源于stack exchange,提问作者Dmitry

