Vercel 的自定义域名体验是行业里最干净的之一,但”干净”不等于”零陷阱”。本文给你确切的 DNS 记录(包括 Vercel 现在按项目分配的专属 CNAME)、可直接粘贴的 dig + curl 验证,以及”域名死活不变绿”客服帖里约 90% 都中招的 5 个错误。内容更新至 2026 年 6 月。
一句话总结
- 在 Vercel 里加上
yourdomain.com(apex),接受弹出的www提示。 - 复制 Vercel 控制台给你看的记录,不要照搬博客里的旧记录——CNAME 现在每个项目都不一样。
- apex 用 A 记录(
76.76.21.21),www用项目专属 CNAME,两条都设为 DNS-only。 - 用
dig验证,等绿勾,再用curl -vI确认 SSL。 - 选一个主域(apex 或 www),另一个会自动 308 跳过去。
证书卡住时,原因几乎总是:旧记录没清、Cloudflare 橙云代理开着,或 CAA 记录把 Let’s Encrypt 挡在外面。
开始前你需要什么
- 一个已经在
*.vercel.app跑通的 Vercel 项目。 - 一个你能控制的 registrar 上的域名(Cloudflare、Porkbun、Namecheap 等)。
- 能在该 registrar 改 DNS(控制台或 API)。
- 在加任何一个变体之前就先定好主域(apex
yourdomain.com还是www.yourdomain.com)。
给独立开发者的一个许可证提醒:截至 2026 年 6 月,Vercel 的 Hobby 套餐免费但仅限非商业用途。如果你的自定义域名用于产品、付费客户或任何产生收入的场景,Vercel 条款要求升到 Pro 套餐($20/席位/月)。Hobby 每个项目最多挂 50 个自定义域名,但这不改变”非商业”这条规则。如果许可证对你重要,可看Firebase 自定义域名——Firebase 的免费 Spark 套餐允许商业使用。
动手改之前
- 切换前 24 小时把现有 A/CNAME 记录的 TTL 调到 300s,让旧解析尽快过期。
- 删掉仍指向旧 host 的 A/CNAME 记录。残留记录会让 DNS 在新旧源站之间来回轮询。
- 确认你的域名 DNS 真的在你以为的地方托管(下面第 1 个坑:DNS 分裂)。
实操步骤
1. 在 Vercel 加域名
Project → Settings → Domains → 输入 yourdomain.com → Add Domain。加 apex 域名时,Vercel 会提示你顺带加上 www 前缀并建议把其中一个跳转到另一个。接受它。
2. 看 Vercel 给你的记录(这点变了)
大多数过时教程就错在这里。截至 2026 年,控制台给每个项目分配的是专属 CNAME,不再是那个通用的:
apex(yourdomain.com):
Type Name Value TTL
A @ 76.76.21.21 300
www(www.yourdomain.com):
Type Name Value TTL
CNAME www d1d4fc829fe7bc7c.vercel-dns-017.com. 300
A 记录的值 76.76.21.21 是 Vercel 的通用 anycast IP,apex 至今仍用它。而 CNAME 目标是按项目分配的——你看到的会像 d1d4fc829fe7bc7c.vercel-dns-017.com,而不是 cname.vercel-dns.com。旧的 cname.vercel-dns.com 仍能解析,但请复制你自己控制台打印的那个值。如果用 Vercel CLI,随时可以打印确切记录:
vercel domains inspect yourdomain.com
为什么 apex 用 A、www 用 CNAME?DNS RFC 1034 禁止在区域 apex 上放 CNAME(apex 本身就需要 NS/SOA 记录),所以 apex 用 A 记录,只有子域用 CNAME。
3. 在 registrar 配上这些记录
任何 registrar 用控制台都行。如果你脚本化 Cloudflare,关键是 proxied: false:
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
--data '{"type":"A","name":"@","content":"76.76.21.21","ttl":300,"proxied":false}'
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
--data '{"type":"CNAME","name":"www","content":"d1d4fc829fe7bc7c.vercel-dns-017.com","ttl":300,"proxied":false}'
proxied: false 让 Cloudflare 保持 DNS-only(灰云)。橙云代理会拦截 HTTPS,导致 Vercel 无法完成 Let’s Encrypt 的 HTTP-01 验证,于是你会看到 “Failed to Generate Cert” / “Invalid Configuration”。至少在证书签发前保持灰云。
4. 去 Vercel 刷新前,先确认 DNS 已传播
dig +short A yourdomain.com @1.1.1.1
# 76.76.21.21
dig +short CNAME www.yourdomain.com @1.1.1.1
# d1d4fc829fe7bc7c.vercel-dns-017.com.
如果 dig 还返回旧 host,说明 TTL 没过期。等着就行——这时别急着在 Vercel 里删了域名重加。
5. 等 “Valid Configuration”,再确认 SSL
Vercel 每几分钟轮询一次,DNS 一旦正确就通过 HTTP-01 自动签发 Let’s Encrypt 证书,通常几分钟内完成。确认证书的 subject 和 issuer:
curl -vI https://yourdomain.com 2>&1 | grep -E 'subject:|issuer:|HTTP'
# subject: CN=yourdomain.com
# issuer: C=US, O=Let's Encrypt, CN=...
# HTTP/2 200
如果 DNS 正确却卡在 “Invalid Configuration” 超过一小时,通常是 CAA 记录没授权 Let’s Encrypt(见第 5 个坑)。
6. 确认非主域上的 308 跳转
apex 和 www 都加好后,Vercel 会把非主域 308 跳转到你选的那个。验证方向:
curl -sI https://www.yourdomain.com | head -3
# HTTP/2 308
# location: https://yourdomain.com/
7. 把 canonical、sitemap、og:url 都指向主域
在 Astro 布局里:
<link rel="canonical" href={`https://yourdomain.com${Astro.url.pathname}`} />
然后 grep 构建产物,揪出任何漏写成错误 host 的 URL:
grep -ROIE 'rel="canonical"|og:url' dist | grep -v 'https://yourdomain.com' | head
# 任何一行 = 漏(vercel.app、www 变体,或 http)
8.(可选)保留 Cloudflare 代理
如果你想用橙云做缓存/WAF,等 Vercel 签完 SSL 再开。然后在 Cloudflare 把 SSL/TLS 模式设为 “Full (strict)“。Full 能用但不校验源站证书;Flexible 会造成无限重定向,因为 Cloudflare 用 HTTP 回源、而 Vercel 强制 HTTPS。
验证清单
| 检查项 | 命令 / 位置 | 期望结果 |
|---|---|---|
| apex 解析 | dig +short A yourdomain.com | 76.76.21.21 |
| www 解析 | dig +short CNAME www.yourdomain.com | 你的 *.vercel-dns-017.com 目标 |
| 都 DNS-only | Cloudflare DNS 标签 | 两条记录都是灰云 |
| 证书有效 | curl -vI https://yourdomain.com | subject = 域名,issuer = Let’s Encrypt |
| 跳转生效 | curl -sI https://www.yourdomain.com | 308 → 主域 |
| canonical 干净 | 对 dist 做 grep | 无非主域 URL |
| 都绿勾 | Vercel → Domains | apex 和 www 都绿勾 |
卡住域名背后的 5 个坑
- DNS 分裂。 你把 nameserver 改到了 registrar,记录却加在别处(或反过来)。Vercel 啥也看不到。用
dig NS yourdomain.com确认到底哪家在应答,把记录加到那边。 - 签发期间开着 Cloudflare 橙云。 代理拦截 HTTP-01 验证 →“Failed to Generate Cert”。证书签好前把记录设为 DNS-only(灰云)。
- 只加了 apex 或只加了 www。 另一半流量撞 404 或无跳转。两个都加,让 Vercel 处理 308。
- 旧记录 TTL 太长没过期。 你切了 DNS,但旧解析还在缓存里。切换之前就把 TTL 调到 300s,然后等旧 TTL 过期。
- CAA 记录挡住 Let’s Encrypt。 如果你的区域有 CAA 记录,它必须授权 Let’s Encrypt,否则签发会无声失败、显示 “Invalid Configuration”。加上或修正:
Type Name Value
CAA @ 0 issue "letsencrypt.org"
用 dig +short CAA yourdomain.com 查现有 CAA。完全没有 CAA 记录是没问题的(任何 CA 都可签发);只列了其他 CA 的 CAA 才会让 Vercel 出问题。
上线后
- Search Console:对新 host 跑 URL Inspection;如果该资源此前已验证,1–2 周内应显示 “URL is on Google”。
- Lighthouse:HTTPS 正常加载、无 mixed-content 警告。
- Vercel → Domains:apex 和 www 都是绿勾。
FAQ
- SSL 到底多久签? DNS 正确后通常几分钟,因为 Vercel 走 Let’s Encrypt 的 HTTP-01 验证。卡过一小时的话,重新核对 CNAME 目标、代理是否关掉、以及 CAA 记录。
- 控制台显示了个奇怪的 CNAME,像
d1d4fc...vercel-dns-017.com,对吗? 对。截至 2026 年每个项目都有专属 CNAME 目标。照你控制台显示的复制;旧的通用cname.vercel-dns.com仍能解析,但已不是 Vercel 现在发给你的那个。 - 主域选 apex 还是 www? 2026 年两个都行。选一个,全站
canonical、sitemap、og:url保持一致。Vercel 文档倾向跳转到www,但 apex 同样可以。 - 一个项目能挂多个域名吗? 能,但要选一个 canonical、其余 308 跳过去,避免重复内容 SEO 问题。
- 免费 Hobby 套餐能用自定义域名吗? 能加,但 Hobby 仅限非商业用途。产生收入的站点需要 Pro($20/席位)。想要免费的商业方案,Firebase Hosting 的 Spark 套餐允许商业使用。
- 同一个域名挂 Vercel 没问题、挂 Firebase 却 “needs setup”? Vercel 直接从 A/CNAME 记录签发证书;Firebase 需要先单独完成 TXT 所有权验证(域名在 Vercel 能用、Firebase 不行)。