You added a custom domain on Vercel / Cloudflare Pages / Netlify / Firebase. The dashboard says “Provisioning SSL certificate”, “Issuing certificate”, or “Pending validation” — and hours later (or even a day or two) it’s still not green. Visiting https://yourdomain.com returns NET::ERR_CERT_AUTHORITY_INVALID or just won’t connect. Most hosts use Let’s Encrypt (or Google Trust Services) underneath, and certificate issuance itself takes seconds. When it stalls it’s almost always wrong DNS, a CAA record blocking the CA, or the host’s HTTP-01 challenge being blocked by your forced-HTTPS / WAF rules. This article runs the checks in DNS → CAA → host-specific order.
Common causes
Ordered by hit rate.
1. DNS hasn’t propagated, or was pointed wrong
The most common. After adding the A / CNAME at the registrar, TTL hasn’t expired yet, or the record type / value is wrong (apex domain can’t be a CNAME but was set to one; A points at a stale IP). The host can’t verify domain ownership.
The host dashboard usually shows the exact expected values — e.g. Vercel wants an apex A record to 76.76.21.21 and www CNAME to cname.vercel-dns.com.
How to spot it:
dig +short yourdomain.com
dig +short www.yourdomain.com
# Compare against the host's expected IP / CNAME
2. CAA record blocks the host’s CA
The apex has a CAA record restricting who can issue certs (e.g. only digicert.com), but the host issues from letsencrypt.org or pki.goog. The CA reads CAA, refuses to issue, and the host sits in Pending forever.
Typical trigger: migrated from an enterprise DNS platform and the old CAA records came along.
How to spot it:
dig CAA yourdomain.com +short
# Example output: 0 issue "digicert.com"
# If letsencrypt.org / pki.goog is not in the allow list, you need to add it
3. HTTP-01 challenge blocked by forced HTTPS / WAF
Let’s Encrypt defaults to HTTP-01: it drops a file at http://yourdomain.com/.well-known/acme-challenge/TOKEN and fetches it. If you have Cloudflare’s “Always Use HTTPS” on or SSL mode set to “Full (strict)”, the HTTP request gets 301’d to HTTPS — but HTTPS isn’t ready yet, and the challenge fails.
WAF / firewall rules blocking .well-known/ cause the same issue.
How to spot it:
curl -I http://yourdomain.com/.well-known/acme-challenge/test
# If it returns 301 → https, the redirect is killing the challenge
4. Wildcard / old cert conflict
The domain previously had a wildcard *.yourdomain.com issued by another host, or multiple projects share the same subdomain and issuance requests collide. Vercel occasionally reports Failed to issue certificate: too many certificates already issued for this exact set of domains (Let’s Encrypt rate limit: 5 per exact set per 7 days).
How to spot it: Search crt.sh for the domain and count how many certs have been issued in the last 7 days.
5. DNSSEC misconfigured at the registrar
DNSSEC is enabled but the DS record never made it to the registry, or the signing chain is broken. Recursive resolvers get SERVFAIL and the CA’s validation also fails.
How to spot it:
dig yourdomain.com +dnssec
# Check for SERVFAIL; or use https://dnsviz.net for a visual check
Shortest path to fix
Step 1: Check DNS from multiple regions
Use multiple global resolvers — don’t trust your local cache:
# Command line
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 tool (recommended)
# https://www.whatsmydns.net/ ~20 nodes globally
Every resolver should return the host’s expected IP / CNAME. If only some do, propagation is still in progress — wait for TTL to expire (usually 1-4 hours; if previous TTL was 86400, up to 24 hours).
Host-expected records quick reference:
| Host | Apex (@) | Subdomain (www) |
|---|---|---|
| Vercel | A 76.76.21.21 | CNAME cname.vercel-dns.com |
| Cloudflare Pages | CNAME flattening / A | CNAME your-project.pages.dev |
| Netlify | A 75.2.60.5 | CNAME your-project.netlify.app |
| Firebase Hosting | Two A records (shown in dashboard) | CNAME or A |
| GitHub Pages | Four A records (185.199.108.153, etc.) | CNAME username.github.io |
Step 2: Inspect and fix CAA
dig CAA yourdomain.com +short
If records exist but exclude your host’s CA, add at the DNS provider:
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"
If no CAA records exist at all, any CA can issue — no change needed. CAA is checked at the registered domain (apex); subdomains inherit.
Step 3: Temporarily disable forced HTTPS / allow .well-known/
If you’re using Cloudflare as DNS + proxy:
- SSL/TLS → Overview → temporarily switch to “Flexible” or “Off” (during validation only)
- Rules → Page Rules → add:
yourdomain.com/.well-known/*→ Cache Level: Bypass + SSL: Flexible
Or just disable Cloudflare’s orange cloud (DNS only) until the host completes issuance, then re-enable proxied.
WAF rules: whitelist .well-known/acme-challenge/* to bypass all challenge / firewall checks.
Step 4: Trigger re-validation in the host dashboard
Once DNS and CAA are fixed, the host won’t always retry on its own. Force it:
- Vercel: Project → Settings → Domains → ⋯ menu → Refresh
- Cloudflare Pages: Custom domains → domain → Retry verification
- Netlify: Domain settings → HTTPS → Renew certificate
- Firebase: Hosting → Custom domains → remove and re-add (cleanest)
Watch the dashboard — it should flip from Pending to Active within 1-5 minutes.
Step 5: Still stuck? Check issuance rate limits and DNSSEC
# Count certs issued in the last 7 days (Let's Encrypt: 5 per exact set per 7 days)
# Open in browser
https://crt.sh/?q=yourdomain.com
# Check DNSSEC health
dig yourdomain.com +dnssec
# Or https://dnsviz.net/d/yourdomain.com/dnssec/
Rate-limited: wait 7 days or switch challenge type (some hosts support DNS-01). DNSSEC errors: disable DNSSEC at the registrar and rerun.
Prevention
- Before adding any new domain,
dig CAA yourdomain.comand confirm CAA allows the host’s CA before touching the dashboard - Keep all DNS records in one place (registrar or one dedicated DNS provider) — don’t split half on Cloudflare and half on Route 53
- Set short TTL (300-1800s) for critical domains so emergency switches don’t take hours to propagate
- When adding a domain, turn off Cloudflare proxy / Always HTTPS first; turn them back on after the cert is issued
- Monitor cert expiry: schedule
curl -vI https://yourdomain.com 2>&1 | grep expireand alert at 30 days remaining
Related
Tags: #Hosting #Debug #Troubleshooting