I helped someone solve an frustrating issue with their custom domain creation this morning, and wanted to share the solution for anyone else stumbling upon it. All four domains were stuck on Step 4: “TLS cert creation verification”. Since they worked for a large enterprise software vendor, I suspected corporate DNS restrictions might be the culprit.
The Root Cause: CAA Records
My hunch was right—their CAA (Certificate Authority Authorization) records were blocking Cloudflare from issuing certificates. I recalled Cloudflare using Let’s Encrypt, but a quick look at Add a Custom Domain in the Vendor Portal reminded me that they have a few partners for certificates and may use any of them (thanks @Amber for the nudge toward the docs).
CAA records work to whitelist which certificate authorities are allowed to issue certificates for your domain. Our custom domains are served from Cloudflare, and they use Let’s Encrypt (among other partners) to issue certificates. Since Let’s Encrypt is a good actor, if they aren’t on the list, they don’t issue the certificates.
How we figured out
I started out by getting the CAA records for the company domain.
$ dig sample.io CAA
; <<>> DiG 9.10.6 <<>> sample.io CAA
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 709
;; flags: qr rd ra; QUERY: 1, ANSWER: 10, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;sample.io. IN CAA
;; ANSWER SECTION:
sample.io. 30 IN CAA 0 issue "globalsign.com"
sample.io. 30 IN CAA 0 issue "digicert.com"
sample.io. 30 IN CAA 0 Issuewild "quovadisglobal.com"
sample.io. 30 IN CAA 0 issue "identrust.com"
sample.io. 30 IN CAA 0 issue "pki.goog"
sample.io. 30 IN CAA 0 Issuewild "identrust.com"
sample.io. 30 IN CAA 0 issue "amazon.com"
sample.io. 30 IN CAA 0 Iodef "mailto:infosec@cisco.com"
sample.io. 30 IN CAA 0 issue "quovadisglobal.com"
sample.io. 30 IN CAA 0 issue "sectigo.com"
;; Query time: 32 msec
;; SERVER: 10.13.6.253#53(10.13.6.253)
;; WHEN: Mon Jun 02 10:44:16 EDT 2025
;; MSG SIZE rcvd: 467
There are ten records there, but I saw none of them were letsencrypt.org
(and some of the other partners are missing). I felt a little surge of victory, but then I remembered something important: CAA records cascade down subdomains. You need to check each level of your domain hierarchy.
So I started checking the subdomains
$ dig security.sample.io CAA
; <<>> DiG 9.10.6 <<>> security.sample.io CAA
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8492
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;security.sample.io. IN CAA
;; ANSWER SECTION:
security.sample.io. 30 IN CAA 0 issue "amazon.com"
security.sample.io. 30 IN CAA 0 issue "identrust.com"
security.sample.io. 30 IN CAA 0 issue "letsencrypt.org"
;; Query time: 41 msec
;; SERVER: 10.13.6.253#53(10.13.6.253)
;; WHEN: Mon Jun 02 10:46:49 EDT 2025
Interestingly, this level does allow Let’s Encrypt, which I thought a the time was all that I needed.
security.sample.io. 30 IN CAA 0 issue "letsencrypt.org"
I though I might be runnuing down the wrong path, but I had to keep that cascading in mind.
$ dig product.security.sample.io CAA
; <<>> DiG 9.10.6 <<>> product.security.sample.io CAA
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59261
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;product.security.sample.io. IN CAA
;; ANSWER SECTION:
product.security.sample.io. 30 IN CAA 0 issue "identrust.com"
product.security.sample.io. 30 IN CAA 0 issuewild "amazon.com"
product.security.sample.io. 30 IN CAA 0 issuewild "amazonaws.com"
product.security.sample.io. 30 IN CAA 0 issuewild "amazontrust.com"
product.security.sample.io. 30 IN CAA 0 issuewild "awstrust.com"
;; Query time: 18 msec
;; SERVER: 10.13.6.253#53(10.13.6.253)
;; WHEN: Mon Jun 02 10:48:37 EDT 2025
;; MSG SIZE rcvd: 371
Aha! This level blocks Let’s Encrypt entirely. Based on what I’ve seen with my custom domains in the past, No Let’s Encrypt = no certificates. Just to be complete, I checked one of the specific domains and saw there were not CAA certificates for it.
The Solution
Changing enterprise DNS records can be a nightmare, especially when security gets involved. For minimum impact (an hopefully a quicker review turnaround), I recommended adding host-specific CAA records for all four custom domains. If you’re in a smaller company, you may be able to update one of the subdomain levels or even your TLD.
To cover all the potential issuers, they need the records in the following example zone file:
$ORIGIN product.security.sample.io
$TTL 3600
charts IN CAA 0 issue "letsencrypt.org" ; registry
charts IN CAA 0 issue "pki.goog; cansignhttpexchanges"
charts IN CAA 0 issue "ssl.com"
charts IN CAA 0 issue "amazon.com"
charts IN CAA 0 issue "cloudflare.com"
charts IN CAA 0 issue "google.com"
charts IN CAA 0 iodef "mailto:security@sample.io" ; consistent with the TLD
images IN CAA 0 issue "letsencrypt.org" ; proxy service
images IN CAA 0 issue "pki.goog; cansignhttpexchanges"
images IN CAA 0 issue "ssl.com"
images IN CAA 0 issue "amazon.com"
images IN CAA 0 issue "cloudflare.com"
images IN CAA 0 issue "google.com"
images IN CAA 0 iodef "mailto:security@sample.io" ; proxy service
enterprise IN CAA 0 issue "letsencrypt.org" ; enterprise portal
enterprise IN CAA 0 issue "pki.goog; cansignhttpexchanges"
enterprise IN CAA 0 issue "ssl.com"
enterprise IN CAA 0 issue "amazon.com"
enterprise IN CAA 0 issue "cloudflare.com"
enterprise IN CAA 0 issue "google.com"
enterprise IN CAA 0 iodef "mailto:security@sample.io" ; enterprise portal
updates IN CAA 0 issue "letsencrypt.org" ; app service
updates IN CAA 0 issue "pki.goog; cansignhttpexchanges"
updates IN CAA 0 issue "ssl.com"
updates IN CAA 0 issue "amazon.com"
updates IN CAA 0 issue "cloudflare.com"
updates IN CAA 0 issue "google.com"
updates IN CAA 0 iodef "mailto:security@sample.io" ; app service
If your custom domains are stuck on certificate verification, grab your favorite DNS tool and start _dig_ging for CAA records. You might just find your answer buried in corporate DNS policies.