自定义域名 SSL 一直在生成中:3 个原因 + 修复路径

域名绑了几小时 SSL 还没好——通常是 DNS 或 CAA。

在 Vercel / Cloudflare Pages / Netlify / Firebase 上把自定义域名加进去之后,dashboard 一直显示 “Provisioning SSL certificate”、“Issuing certificate” 或 “Pending validation”,几小时甚至一两天都不变绿——访问 https://yourdomain.com 浏览器报 NET::ERR_CERT_AUTHORITY_INVALID 或直接连不上。多数宿主背后都是 Let’s Encrypt(或 Google Trust Services),证书签发本身只需要几十秒,卡住几乎一定是 DNS 没指对、CAA 把 CA 拦了、或者宿主用 HTTP-01 challenge 但你开了强制 HTTPS 重定向阻断验证。本文按”先 DNS、再 CAA、再宿主特有问题”的顺序排查。

常见原因

按命中率从高到低排。

1. DNS 还没传播,或者根本指错了

最常见。在域名服务商加 A / CNAME 后,TTL 没到、或者类型 / 值填错(比如根域名只能 A 不能 CNAME,但写了 CNAME;或 A 指向了过时的 IP),宿主无法验证域名归属。

宿主 dashboard 里通常会显示具体期望值,例如 Vercel 要求根域名 A 记录指向 76.76.21.21,子域名 CNAME 指向 cname.vercel-dns.com

如何判断

dig +short yourdomain.com
dig +short www.yourdomain.com
# 比对宿主期望的 IP / CNAME

2. CAA 记录把宿主的 CA 拦了

域名根上有 CAA 记录限定了能签证书的 CA(如只允许 digicert.com),但宿主用的是 letsencrypt.orgpki.goog,CA 一查 CAA 直接拒绝签发,宿主就一直停在 Pending。

典型情况:从企业 DNS 平台迁过来,老 CAA 记录留着没改。

如何判断

dig CAA yourdomain.com +short
# 输出示例:0 issue "digicert.com"
# 若不包含 letsencrypt.org / pki.goog,需要补

3. HTTP-01 challenge 被强制 HTTPS / WAF 拦住

Let’s Encrypt 默认用 HTTP-01:在 http://yourdomain.com/.well-known/acme-challenge/TOKEN 放一个验证文件。如果你在 Cloudflare 开了 “Always Use HTTPS” 或 SSL 模式设成 “Full (strict)“,HTTP 请求被强制 301 到 HTTPS,但 HTTPS 还没好,验证就 fail。

WAF / 防火墙规则拦 .well-known/ 路径也是同类问题。

如何判断

curl -I http://yourdomain.com/.well-known/acme-challenge/test
# 如果是 301 → https,说明被强制跳了

4. 通配符证书 / 老证书冲突

域名之前在另一家宿主已经签过通配符 *.yourdomain.com,或者多个项目同时绑同一个子域名,签发请求互相覆盖。Vercel 偶尔会报 Failed to issue certificate: too many certificates already issued for this exact set of domains(Let’s Encrypt 7 天限速 5 张)。

如何判断:去 crt.sh 搜域名,看最近 7 天签了多少张同名证书。

5. 域名注册商 DNSSEC 配错

启用了 DNSSEC 但 DS 记录没传到 registry,或者签名链断了,递归解析器拿到 SERVFAIL,CA 验证也跟着失败。

如何判断

dig yourdomain.com +dnssec
# 看是否返回 SERVFAIL;或用 https://dnsviz.net 在线检查

最短修复路径

Step 1:多地区验证 DNS 解析

用全球多地的 resolver 同时查,避免只看到本地缓存:

# 命令行
dig +short yourdomain.com @8.8.8.8       # Google
dig +short yourdomain.com @1.1.1.1       # Cloudflare
dig +short yourdomain.com @9.9.9.9       # Quad9

# Web 工具(建议)
# https://www.whatsmydns.net/  全球 ~20 个节点

期望所有 resolver 都返回宿主要求的 IP / CNAME。如果只有部分返回,就是还在传播,等 TTL 过期(一般 1-4 小时;上次 TTL 是 86400 则要 24 小时)。

宿主期望速查:

宿主根域名 (@)子域名 (www)
VercelA 76.76.21.21CNAME cname.vercel-dns.com
Cloudflare PagesCNAME flattening / ACNAME your-project.pages.dev
NetlifyA 75.2.60.5CNAME your-project.netlify.app
Firebase HostingA 两条(dashboard 显示)CNAME 或 A
GitHub PagesA 四条 (185.199.108.153 等)CNAME username.github.io

Step 2:检查 + 修 CAA 记录

dig CAA yourdomain.com +short

如果有记录但缺你的 CA,在 DNS 服务商加:

yourdomain.com.  3600  IN  CAA  0 issue "letsencrypt.org"
yourdomain.com.  3600  IN  CAA  0 issue "pki.goog"
yourdomain.com.  3600  IN  CAA  0 issuewild "letsencrypt.org"

如果根本没 CAA 记录,任何 CA 都可以签,不用加。注意 CAA 只看根域名(registered domain),子域名继承。

Step 3:临时关闭强制 HTTPS / 放行 .well-known/

如果用 Cloudflare 当 DNS + proxy:

  • SSL/TLS → Overview → 暂时改成 “Flexible” 或 “Off”(仅验证期间)
  • Rules → Page Rules → 新建:yourdomain.com/.well-known/* → Cache Level: Bypass + SSL: Flexible

或者直接关掉 Cloudflare 的 orange cloud(DNS only),等宿主把证书签下来再开回 proxied。

WAF 规则:在 .well-known/acme-challenge/* 路径上加白名单,绕过所有 challenge / firewall 检查。

Step 4:在宿主 dashboard 触发重新验证

DNS 和 CAA 都修好之后,宿主不一定会立刻重试。手动触发:

  • Vercel:Project → Settings → Domains → 域名右侧 ⋯ → Refresh
  • Cloudflare Pages:Custom domains → 域名 → Retry verification
  • Netlify:Domain settings → HTTPS → Renew certificate
  • Firebase:Hosting → Custom domains → 删了重新添加(最干净)

观察 dashboard 状态,正常情况 1-5 分钟内会从 Pending 变 Active。

Step 5:还卡住就排查证书签发限速 + DNSSEC

# 查最近 7 天签发了多少张同名证书(Let's Encrypt 限 5 张 / 7 天)
# 浏览器打开
https://crt.sh/?q=yourdomain.com

# 查 DNSSEC 是否健康
dig yourdomain.com +dnssec
# 或用 https://dnsviz.net/d/yourdomain.com/dnssec/

如果触了限速,等 7 天或换 challenge type(部分宿主支持 DNS-01);DNSSEC 报错就在 registrar 关掉 DNSSEC 重新跑一次。

预防建议

  • 加新域名之前先 dig CAA yourdomain.com,确认 CAA 允许宿主的 CA,再去 dashboard 操作
  • 所有 DNS 记录维护在一个地方(registrar 或一家专业 DNS 服务商),别一半在 Cloudflare 一半在 Route 53
  • 给关键域名 TTL 设短(300-1800 秒),紧急切换时不用等几小时传播
  • 加完域名先把 Cloudflare proxy / Always HTTPS 关掉,等证书签好再开
  • 监控证书到期:curl -vI https://yourdomain.com 2>&1 | grep expire 设定期任务,提前 30 天告警

相关阅读

标签: #部署 / 托管 #排查 #排查