绑了自定义域名但 SSL 一直 pending:按命中率排查 + 对症修复

Vercel / Firebase / Netlify 绑域后 SSL 卡在 pending。先用 dig 验 DNS、把 Cloudflare 橙云改成灰云,再按清单排查 CAA、记录类型、AAAA、HSTS。

你在 Vercel / Firebase / Netlify 绑了自定义域名。控制台显示 “SSL certificate pending”,或者 HTTP 能正常打开、但 HTTPS 报 NET::ERR_CERT_AUTHORITY_INVALID。30 分钟过去了,1 小时过去了。

最快的修法: 这三个平台都用 Let’s Encrypt,它要满足两个条件才发证——(1) 你的域名能解析到平台(而不是旧主机);(2) 平台能完成一次 ACME challenge,通常是 HTTP-01,也就是去拉 http://yourdomain.com/.well-known/acme-challenge/<token>。所以先查两件事:用 dig 确认 DNS 指向平台、不是旧主机;如果域名走了 Cloudflare 代理(橙云),把它改成 “DNS only”(灰云)。大部分卡住的证书都出在这两条。下面是排查其余情况的有序清单。

一个让人安心的事实:1 小时内显示 “pending” 是正常的。Netlify 在头 24 小时里每 10 分钟重试一次;Firebase 最长可能要 24 小时。DNS 没真正搞对之前,别急着大动干戈。

你属于哪一种?

症状最可能的原因跳到
dig 返回的是旧主机 IPDNS 没传播 / 记录类型错原因 1、4
域名走 Cloudflare,证书一直发不出橙云代理截了 challenge原因 3
控制台报错里提到 CAACAA 记录挡了 Let’s Encrypt原因 2
Firebase 状态卡在 “Needs setup” / pending有冲突的 A / CNAME / AAAA 记录原因 4、6
控制台说 “Domain not verified”没加所有权 TXT原因 5
证书已发但浏览器还报错浏览器里残留的 HSTS原因 7

常见原因

按命中率从高到低排列。

1. DNS 还没传播

发证器要去拉 http://yourdomain.com/.well-known/acme-challenge/<token>。DNS 还没到发证器用的 resolver,challenge 就失败,平台稍后再重试。

怎么判断:

dig yourdomain.com +short

还没返回平台的 IP/CNAME,就是没传播。用本机 resolver 之外的角度再交叉验证一下:dnschecker.org 或 Google 的 Admin Toolbox Dig——各地区传播进度不一致,发证器查到的那台 resolver 可能还没更新。

2. CAA 记录挡了 Let’s Encrypt

如果你的 zone 里有 0 issue "digicert.com" 这种 CAA 记录,就只有 DigiCert 能发证,Let’s Encrypt 的请求会被拒,平台会显示 CAA 错误。

怎么判断:

dig CAA yourdomain.com +short

看到 CAA 记录、且没有一条允许 letsencrypt.org,就是这条。注意:完全没有 CAA 记录时任何 CA 都能发证,这才是安全的默认值。(apex 和父级 zone 都要看,因为 CAA 是继承的。)

3. Cloudflare 代理(橙云)截了 challenge

Cloudflare 代理你的 DNS(橙云)时,它自己来终止 TLS、挡在源站前面。平台没法把 HTTP-01 的 challenge 响应送出去,Cloudflare 还可能返回缓存的或被重定向的响应,验证就失败。Netlify 把话说得很直白:它”必须自己终止 TLS 才能签发证书”,所以任何挡在前面的服务都得先关掉。

怎么判断: Cloudflare 的 DNS 列表里,apex(和 www)的 A/CNAME 显示橙云(Proxied)。初次发证时必须是灰云(DNS only)。

4. 记录类型错或有残留的冲突记录

每个平台都要求特定的记录类型和值。该用 A 却设成 CNAME(或旧主机留下的残记录),都会挡住发证。

  • Vercel apex:用 A 记录。76.76.21.21 现在仍可用,但 Vercel 现在会按项目生成各自的值——以 Project → Settings → Domains 上显示的为准,或跑 vercel domains inspect yourdomain.comwwwCNAME 指向 cname.vercel-dns.com(较新的项目可能显示 *.vercel-dns-NNN.com 这种目标,以控制台显示的为准)。
  • Netlify apex:A 指向 75.2.60.5wwwCNAME 指向 <你的站点>.netlify.app
  • Firebase:Connect Domain 弹窗里给出的那几条 A 记录,并删掉所有指向别处的 A、CNAME、AAAA——只要还有这类记录存在,Firebase 就无法签发证书。

怎么判断:dig 输出和控制台显示的值逐条对比,记录类型别即兴发挥。

5. 平台还没验证域名所有权

有些场景要求先加一条所有权 TXT 记录才发 SSL。加了域名却跳过验证,控制台就会一直 “pending”。

怎么判断: 在平台控制台找 “Verify ownership”、“Domain not verified” 或 Verify 提示,把它给的 TXT 加上。

6. AAAA 记录指向别处

A 记录指对了,但 AAAA(IPv6)还指向旧主机。发证器可能偏好 IPv6,结果打到错的目标。

怎么判断:

dig AAAA yourdomain.com +short

AAAA 指向别处就删掉或更新。(Firebase 尤其如此:只要有残留的 AAAA,发证就会失败。)

7. 浏览器里残留的 HSTS

证书其实已经发出来了,但浏览器记着旧的 HSTS 条目,只肯走那个坏掉的 HTTPS、不肯回退。这不影响发证——它只是掩盖了”已经成功”的事实。

怎么判断: 控制台都报证书已签发了,浏览器还在 NET::ERR_CERT_AUTHORITY_INVALID。Chrome 里到 chrome://net-internals/#hsts → “Delete domain security policies” 清掉它,再开个无痕窗口试。

最短修复路径

第 1 步:用 dig 验证 DNS

dig yourdomain.com +short            # apex 的 A 记录
dig www.yourdomain.com +short        # 解析 www 的 CNAME 链

两条都应返回平台期望的值。还显示旧主机 IP,就是 DNS 没完全到位——先在 dnschecker.org 上确认全球都传播好了,再做别的。

第 2 步:查 CAA

dig CAA yourdomain.com +short

看到 0 issue "digicert.com" 这种限制性记录,要么加 0 issue "letsencrypt.org",要么把 CAA 记录整条删掉(没有 CAA = 任何 CA 都能发)。如果想安全地把发证锁到某一个平台,就别只锁 CA、而是锁它的 ACME 账号——比如 Netlify 就文档化了一个绑定其 Let’s Encrypt 账号的 accounturi CAA 值。

第 3 步:临时把 Cloudflare 改成 “DNS only”

到 Cloudflare → DNS → Records → 对 apex 和 www,点橙云让它变灰(DNS only)。等 1-2 分钟,再到平台控制台重新触发发证。

证书发出后可以重新开代理(橙云)。续签能穿过 Cloudflare,因为平台会改用 DNS-01 或复用已缓存的授权。如果必须在发证期间保持橙云开着,另一个办法是建一条 Cloudflare Configuration Rule,把 /.well-known/acme-challenge/* 这个路径强制走纯 HTTP 且关掉缓存——但临时改灰云两分钟更简单。

第 4 步:确认记录类型和值跟控制台一致

从平台拉出确切的值,再用 dig 比对:

  • Vercel:vercel domains inspect yourdomain.com(或 Project → Settings → Domains)。
  • Netlify:Domain management 里为你站点显示的记录(apex A 75.2.60.5www CNAME <site>.netlify.app)。
  • Firebase:Connect Domain 弹窗里的 A 记录;其余的 A/CNAME/AAAA 全删。

apex 绝不要用 CNAME——DNS 规范禁止在 zone 根上用 CNAME(RFC 1034)。按你 DNS 服务商的支持,用 AALIAS/ANAME

第 5 步:重新触发发证

修完 DNS / CAA / 代理后:

  • Vercel: Project → Settings → Domains → 点 Refresh,或者删了重加域名。
  • Netlify: Domain management → HTTPSVerify DNS configuration,再点 Provision certificate
  • Firebase: Hosting → 在 Connect Domain 弹窗里点 Verify;之后状态会自己往前走(没有手动发证按钮)。

第 6 步:等,但别来回折腾

多数平台按固定节奏重试:Netlify 头 24 小时每 10 分钟一次;Vercel 几分钟内复查;Firebase 最长 24 小时。不要反复删了重加域名——那会重置你在发证队列里的位置,反而更慢。

第 7 步:怎么确认已经修好

证书生效后,从干净的环境验证,别只看控制台:

curl -svo /dev/null https://yourdomain.com 2>&1 | grep -Ei 'subject:|issuer:|expire'

应该能看到合法的 issuer(比如 Let’s Encrypt / R 系列)和一个未来的过期时间。或者用无痕窗口打开站点——挂锁、没有警告就说明好了。如果浏览器还报错但 curl 正常,那就是命中了缓存的 HSTS(原因 7)。

第 8 步:仍卡住就找平台支持

DNS 正确、CAA 清干净、代理关掉后过了 4 小时以上还 pending,就提工单。附上:

  • 域名
  • dig yourdomain.com +shortdig CAA yourdomain.com +short 的输出
  • 添加域名的时间戳
  • 控制台确切报错/状态的截图

预防建议

  • 没特别理由别设 CAA;要设就把平台可能用的所有 CA 都列上(Let’s Encrypt、Sectigo 等),或者锁平台的 ACME accounturi
  • 初次发证前别开 Cloudflare 代理。
  • 看控制台之前先 dig——DNS 不对,证书发不出来。
  • 严格用平台显示的记录类型和值;apex 绝不用 CNAMEA
  • 把上家主机留下的 A/CNAME/AAAA 残记录都删掉,再把 DNS 指到新家。
  • 给新域名维护一份 domain-setup.md runbook,写清你平台的精确步骤和值。

常见问题

SSL 发证一般要多久? DNS 一旦正确,通常几分钟到 1 小时。Netlify 头 24 小时每 10 分钟自动重试;Firebase 说最长 24 小时。如果 DNS 没问题、又不到 1 小时,就再等等。

都好几个小时了还 pending,最常见的原因是哪个? DNS 没完全传播到发证器用的 resolver,或者域名还在 Cloudflare 橙云后面。用 dnschecker.org 重查 dig,并确认云已经是灰的。

Cloudflare 代理要一直关着吗? 不用。只在初次发证时改成灰云(DNS only),发完再开回来。续签走 DNS-01 或复用缓存授权,开着代理也没问题。

加一条 CAA 记录能加速吗? 只有当原来有一条限制性 CAA 在挡发证时才有用。如果你压根没有 CAA,就别动——任何 CA 都能发。只有在需要显式放行 Let’s Encrypt 时才加 0 issue "letsencrypt.org"

证书显示已签发,但浏览器还报 NET::ERR_CERT_AUTHORITY_INVALID 那是你本机残留的 HSTS/缓存,不是发证问题。清掉 HSTSchrome://net-internals/#hsts),再用无痕窗口试。

apex/根域名能用 CNAME 吗? 不能——DNS 禁止在 zone 根上用 CNAME(RFC 1034)。用 A 记录,或者用 DNS 服务商的 ALIAS/ANAME 扁平化来在根上实现类似 CNAME 的效果。

相关阅读

标签: #排查 #DNS #排查 #SSL