你在 firebase.json 里配了 rewrites,比如 /api/** 转到 Cloud Function、** 转到 SPA 的 index.html,部署后访问 /api/xxx 应该走函数但直接 404;或者 SPA 的深层链接 /dashboard/123 返回 404 而不是 index.html——配置看起来对,结果就是不生效。
Firebase Hosting 的 rewrite 规则有几条”看不见”的优先级:静态文件 > rewrites 第一条匹配 > 404.html。理解这套优先级,才能修对地方。
常见原因
按命中率从高到低:
1. rewrites 顺序错(先匹配先生效)
{
"rewrites": [
{ "source": "**", "destination": "/index.html" }, // ❌ 这条吃掉所有路径
{ "source": "/api/**", "function": "api" } // 永远到不了
]
}
把更具体的放前面。
如何判断:rewrite 配置里 ** 通配在 /api/** 前面?
2. 同路径有静态文件,被静态优先
如果 public/api.html 存在,访问 /api 会拿静态文件而不是 rewrite 到 function。
如何判断:ls public/ 看是否有跟 rewrite source 冲突的文件。
3. 函数 region 跟 rewrite region 不一致
{
"rewrites": [
{ "source": "/api/**", "function": { "functionId": "api", "region": "us-east1" } }
]
}
函数实际部署在 us-central1,rewrite 找 us-east1——404。
如何判断:firebase functions:list 看函数 region,跟 rewrite 里写的对。
4. 函数没真的部署成功
deploy 命令成功,但函数 build 失败被跳过(看 firebase-deploy-permission-denied 类似的 silent fail)。rewrite 指向一个不存在的函数。
如何判断:firebase functions:list 看函数名是否在列表里。
5. 用了 cleanUrls / trailingSlash 改变路径
{
"cleanUrls": true, // /about.html → /about
"trailingSlash": false // /about/ → /about
}
启用这些后,你的 source pattern 也要相应调整,否则匹配不上。
如何判断:config 里有 cleanUrls / trailingSlash,rewrite source 还在用旧路径。
6. firebase.json 改了但没 deploy hosting
只 firebase deploy --only functions 不会更新 rewrite,必须 --only hosting 或全量 deploy。
如何判断:最近一次 firebase deploy 命令带了什么 flag?
最短修复路径
Step 1:rewrites 重排序——具体的放前面
{
"hosting": {
"public": "dist",
"rewrites": [
{ "source": "/api/**", "function": { "functionId": "api", "region": "us-east1" } },
{ "source": "/webhooks/**", "function": "webhookHandler" },
{ "source": "**", "destination": "/index.html" }
]
}
}
规则:从最具体到最通用排序,最后一条才是 SPA fallback。
Step 2:删掉冲突的静态文件
ls public/
# 如果有 api.html、api/index.html 等,移走
mv public/api.html /tmp/
firebase deploy --only hosting
Step 3:对齐函数 region
# 1. 看函数实际 region
firebase functions:list
# 2. firebase.json 里 rewrite region 匹配
{
"source": "/api/**",
"function": {
"functionId": "api",
"region": "asia-east1"
}
}
或者函数代码里改 region 重新部署。
Step 4:确认部署 hosting 配置
firebase deploy --only hosting
# 如果改了 firebase.json 又改了函数:
firebase deploy --only hosting,functions
# 看本次部署的实际 rewrites
firebase hosting:channel:list # 看部署版本
Step 5:用 emulator 本地验证
firebase emulators:start --only hosting,functions
# 访问 http://localhost:5000/api/test
# 看是否走 function
local 正常 = 生产配置 / 部署问题;local 也错 = 配置本身错。
Step 6:用 curl 看实际响应
curl -v https://your-app.web.app/api/test
# 200 + 函数响应 = rewrite 工作了
# 404 + HTML 内容 = 没 rewrite,被静态/SPA fallback 接走
# 404 + 空 = 路径不存在,rewrite 也没匹配
Step 7:复杂规则用 redirects + rewrites 组合
{
"redirects": [
{ "source": "/old-api/**", "destination": "/api/:1", "type": 301 }
],
"rewrites": [
{ "source": "/api/**", "function": "api" },
{ "source": "**", "destination": "/index.html" }
]
}
redirects 优先级最高,会先走。
预防建议
firebase.json加注释(用 jsonc 或外部 doc),rewrite 顺序要写清楚为什么- CI 里加一步:deploy 后跑 curl 测试每个关键 rewrite path
- public/ 目录里别放
api.html、webhooks.html这类容易冲突的名字 - 项目用一个固定 region,函数全部部署到同一个,rewrite 不再手写 region
- firebase.json 改动走 PR review,避免不小心改坏顺序
firebase deploy默认全量,明确知道改了什么时才用--only- 本地用 emulator 跑一遍完整 routing 再 deploy
- 部署后用 production URL 测,不要只信 emulator