你正准备把真实域名指到 Firebase Hosting。dev 没问题、*.web.app 没问题。真正有风险的是切 DNS 这一下:周五手一抖,整个周末就得守着 Search Console 看它报重复 URL、看 SSL 证书签发了一半。先过一遍这 10 项检查,每一项都是哪个独立开发者被坑过至少一次的事。
TL;DR
firebase.json的public要对得上真实 build 目录(Astro / Vite 是dist、Next 静态导出是out),缓存头在 deploy 之前就要配好。- 只选一个主域(apex 或
www),另一个 301 过去。两个都被收录 = 抓取预算被劈成两半。 - 切 DNS 之前先等自定义域名的 SSL 证书签发完。按官方文档,签发最长 24 小时(一般几小时)。
- sitemap 只提交到主域,绝不提
*.web.app,apex +www也不要都提。 - 回滚要练成肌肉记忆:
Hosting → Release history → Rollback,停机一般一分钟以内。
10 步清单
1. firebase.json 对准 build 目录,并锁好缓存头
Astro 的 build 目录是 dist、Next 静态导出是 out、Vite 是 dist。静态内容站直接用下面这份配置——带哈希指纹的资源走一年 immutable 缓存,HTML 走浏览器短 TTL + CDN 较长 TTL,再加 stale-while-revalidate,访客基本不会因为缓存未命中而干等:
{
"hosting": {
"site": "your-firebase-project",
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"cleanUrls": true,
"trailingSlash": true,
"redirects": [
{ "source": "/blog/:slug", "destination": "/articles/:slug", "type": 301 }
],
"headers": [
{
"source": "/_astro/**",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
},
{
"source": "**/*.@(html)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=3600, stale-while-revalidate=86400" }
]
},
{
"source": "**",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
]
}
]
}
}
cleanUrls: true 会去掉 .html 后缀,并把仍带后缀的请求 301 过去。trailingSlash: true 让 Hosting 自动补尾斜杠,保证 canonical 形态统一。哈希资源上的 immutable 直接省掉浏览器的重新校验往返。
2. 确定主域:apex 还是 www
在 Firebase Console → Hosting → Add custom domain 把两个域都加上,非主域设成 redirect。Firebase 向导会针对你的域名给出确切的 A 记录 IP(截至 2026 年 6 月是 199.36.158.100);以控制台显示的为准。apex 做主域时的典型配置:
Type Name Value
A @ 199.36.158.100
CNAME www your-project.web.app
如果你迁移的是已经有流量的域名,用 Advanced Setup(高级设置)而不是 Quick Setup。Advanced Setup 会先验证所有权、先把证书签发好,再让你改 DNS,这样流量一到 Hosting 就能立刻给出正确内容——这才是真正的零停机路径。
3. 切流量前,两个主机名 SSL 都要 OK
按官方说法,DNS 指到 Hosting 后证书签发最长 24 小时(一般几小时)。切过去之前先确认证书已签发:
curl -vI https://yourdomain.com 2>&1 | grep -E 'subject:|issuer:|HTTP'
# subject: CN=yourdomain.com
# issuer: C=US, O=Google Trust Services LLC, CN=...
# HTTP/2 200
看到 subject: CN=*.web.app 说明自定义域证书还没签发——等,不要切 DNS。
4. 测 trailing slash 和 cleanUrls 行为
无痕窗口里把几种路径都打一遍,确认状态码和 firebase.json 规则一致:
curl -sI https://yourdomain.com/about | head -1 # 期望 301 → /about/
curl -sI https://yourdomain.com/about/ | head -1 # 期望 200
curl -sI https://yourdomain.com/missing/ | head -1 # 期望 404,不能是 200
/missing/ 返回 200 说明有个 catch-all 重写把真正的 404 盖掉了,会让 Google 收录一堆垃圾 URL。
5. 线上 URL 跑 Lighthouse
Performance 和 Best Practices 目标做到 90 以上——静态站跑在 Google CDN 上的 Firebase Hosting,这个分很好拿:
npx lighthouse https://yourdomain.com/ \
--only-categories=performance,best-practices,seo \
--chrome-flags="--headless" --quiet
6. sitemap 只提交到主域
*.web.app 不提,apex + www 也不要都提:
Search Console → Sitemaps → 添加新 sitemap
URL: https://yourdomain.com/sitemap-index.xml
7. canonical 和 og:url 全指向生产主域
<link rel="canonical"> 或 Open Graph 标签里残留一个 *.web.app 或预发布 URL,会悄悄把真实域名挤出索引。build 产物里扫一遍:
grep -ROIE 'rel="canonical"|og:url' dist | grep -v yourdomain.com | head
# 任何输出 = HTML 里漏了非主域
8. 上线前先把回滚演练一遍
Firebase 会把历史 release 留在 Hosting 存储里(保留多少个可在 Release storage settings 里设上限)。现在就练一遍回滚,真出事时才能下意识操作:
firebase hosting:releases:list
# release-1 (当前) 2026-05-22 14:02 abc123
# release-0 2026-05-22 13:40 def456
firebase deploy --only hosting # 每次 deploy 都会生成一个新 release
# 控制台:Hosting → Release history → ⋮ → Rollback
回滚是用旧内容生成一个新 release,不会删任何东西,所以随时回滚都安全。
9. 设预算告警(Blaze 用户)
免费的 Spark 套餐含 10 GB 存储、360 MB/天传输,且允许商用。Blaze(按量付费)享有同样的免费额度,超出后带宽约 $0.20/GiB、存储约 $0.10/GB。在 GCP Console → Billing → Budgets & alerts,按预期花费的 50% 和 90% 各设一次告警。纯静态站基本不会触发——但失控的脚本或被盗链的资源可能给你惊喜。
10. 切 DNS 前,对 *.web.app URL 做最后检查
趁站点还只在 *.web.app 上时跑这几条:
curl -s https://your-project.web.app/sitemap-index.xml | grep -c '<loc>'
curl -s https://your-project.web.app/robots.txt | head
curl -sI https://your-project.web.app/ | grep -i cache-control
URL 数对得上、robots.txt 里引了 sitemap、缓存头和第 1 步一致——就可以切 DNS 了。
速查:时长与额度(2026 年 6 月)
| 项目 | 大致情况 |
|---|---|
| SSL 证书签发 | DNS 指向 Hosting 后最长 24 小时;一般几小时 |
| DNS 传播 | 提前调小 TTL 的话多数人 5-30 分钟;完全传播最长 24 小时 |
| Spark 免费额度 | 10 GB 存储、360 MB/天传输,允许商用 |
| Blaze 超额 | 带宽约 $0.20/GiB、存储约 $0.10/GB(免费额度之上) |
| 回滚停机 | 一般一分钟以内 |
| 保留的历史版本数 | 可在 Release storage settings 里配置 |
容易踩的坑
- 没定主域,结果 apex 和
www都被 Google 收录。 - sitemap 交到了非主域——浪费抓取预算。
- 换新域名后忘了改
og:url。 - 自定义域证书还没签发就切 DNS,给首批访客喂证书警告。
- 没演练回滚就周五上线。
- sitemap 或 canonical 里还留着
*.web.app或预发布 URL。
如果你还没绑真实域名、仍在 preview channel 上,那只用前 5 步——后面几步是讲 DNS 切换的。
FAQ
- 切 DNS 后多久流量稳定? 提前把 TTL 调小的话,5-30 分钟内大部分世界就指到新 host 了。完全传播最长 24 小时。
- 为什么域名还在用旧证书,或弹
*.web.app警告? 自定义域证书还没签发完。Firebase 说 DNS 指向 Hosting 后这最长要 24 小时,不过一般几小时。等一等,切流量前重新跑第 3 步的curl -vI确认。 - 上线后能关掉
*.web.app域名吗? 关不掉,但把 canonical 和og:url都指向自定义域名,搜索 / 社交就不会收*.web.app。 - 需要 robots.txt 吗? 需要。哪怕只写一行 sitemap 链接,也能让爬虫快速找到 sitemap。
- 刚上线发现重大 bug 怎么办?
Hosting → Release history → Rollback回滚到上一个版本,然后修完再部。回滚是用旧内容生成新 release,停机一般一分钟以内。 - apex 还是
www——哪个做主域更稳? 都行。apex(yourdomain.com)在搜索结果里更干净;www以后想套第三方 CDN 会稍微方便些。唯一的错误答案是两个都收录。