Dev.to Security 🔐 Cybersecurity 👁 0 📖 7 min read

CAA Records and CT Logs: Control Who Issues Your Certs

Any of the publicly trusted certificate authorities can issue a valid HTTPS certificate for your domain, as long as someone passes its domain validation check. You did not choose most of those CAs, and by default your DN

CAA Records and CT Logs: Control Who Issues Your Certs

Any of the publicly trusted certificate authorities can issue a valid HTTPS certificate for your domain, as long as someone passes its domain validation check. You did not choose most of those CAs, and by default your DNS says nothing about which ones you trust. Two cheap controls change that: a CAA record tells CAs who is allowed to issue, and Certificate Transparency logs let you see every certificate that was issued anyway.

I build PixelValut, a set of browser-side developer and security tools, and this post started when I checked my own domain this morning and found no CAA record at all. I wrote this with help from AI tools and checked every command and claim myself. All outputs below are from commands I ran today, November 2, 2026.

What do CAA records and CT logs actually protect?

Direct answer: A CAA DNS record lists the certificate authorities allowed to issue certificates for your domain, and compliant CAs must check it before issuing. Certificate Transparency logs publicly record every trusted certificate. Use CAA to narrow who can issue, and monitor CT logs to catch any certificate you did not request, including mis-issuance.

Threat scenarios this covers

Scenario What happens CAA helps? CT monitoring helps?
Attacker controls a dangling subdomain Passes HTTP validation at any CA and gets a valid cert Partly: limits which CAs; accounturi limits it further Yes, the new cert appears in logs
A CA mis-issues for your domain Valid cert exists that you never requested Only if that CA honors CAA correctly Yes, this is what CT was built for
Another team buys a cert from a different vendor Shadow certificates, missed renewals, surprise outages Blocks it until the CAA record is updated Yes, you see the vendor and the names
Phishing look-alike domain gets a cert pixe1valut.com style site with a padlock No, it is not your domain Yes, if you watch for name patterns
Stolen private key of a live cert Attacker reuses an existing cert No No, nothing new is issued

The last row matters: neither control helps once a valid key is stolen. That is a revocation and key-management problem.

Read a real CAA record

You do not need dig installed. DNS-over-HTTPS returns JSON you can read with curl and jq:

curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=github.com&type=CAA' \
  | jq -r '.Answer[] | "\(.name)  TTL=\(.TTL)  \(.data)"'
github.com  TTL=3600  0 issue "digicert.com"
github.com  TTL=3600  0 issue "globalsign.com"
github.com  TTL=3600  0 issue "letsencrypt.org"
github.com  TTL=3600  0 issue "sectigo.com"
github.com  TTL=3600  0 issuewild "digicert.com"
github.com  TTL=3600  0 issuewild "letsencrypt.org"
github.com  TTL=3600  0 issuewild "sectigo.com"

Each record has three parts: a flag (0, or 128 for critical), a tag and a value.

Tag Meaning Example value Notes
issue Who may issue any certificate "letsencrypt.org" Multiple records allow multiple CAs
issuewild Who may issue wildcard certificates "digicert.com" If present, it overrides issue for wildcards
iodef Where CAs report blocked requests "mailto:[email protected]" Support for sending reports varies by CA
issue with ; Nobody may issue ";" Useful for domains that never serve HTTPS

Notice that GlobalSign is allowed for normal certificates but not for wildcards. That is the kind of narrowing worth copying.

For a subdomain, the CA walks up the tree until it finds a CAA set, and it follows CNAMEs. Asking for www.github.com returned the CNAME to github.com and then the same seven records. So one record set at the apex covers every subdomain that does not define its own.

My own domain returned nothing:

{"Status":0,"Answer":[]}

An empty answer means no restriction: any trusted CA may issue.

A large open ledger book with blank pages under a brass desk lamp, representing the public append-only logs of certificate transparency
AI-generated image.

Check CT logs before you write the record

Do not guess which CAs you use. CT logs tell you. crt.sh exposes a JSON endpoint:

curl -s 'https://crt.sh/?q=pixelvalut.com&output=json' > crt.json
jq 'length' crt.json
jq -r '[.[].issuer_name] | group_by(.) | map("\(length)  \(.[0])")[]' crt.json
35
4  C=US, O=Let's Encrypt, CN=E5
10  C=US, O=Let's Encrypt, CN=E7
14  C=US, O=Let's Encrypt, CN=E8
4  C=US, O=Let's Encrypt, CN=YE1
3  C=US, O=Let's Encrypt, CN=YE2

Thirty-five log entries, every one from Let's Encrypt intermediates. Most certificates appear twice, once as the precertificate and once as the final certificate, with the same not_before time, so do not mistake those pairs for duplicate issuance.

The live certificate confirms it:

echo | openssl s_client -connect pixelvalut.com:443 -servername pixelvalut.com 2>/dev/null \
  | openssl x509 -noout -issuer -dates
issuer=C=US, O=Let's Encrypt, CN=YE2
notBefore=Sep 27 03:44:38 2026 GMT
notAfter=Dec 26 03:44:37 2026 GMT

You can also see the embedded Signed Certificate Timestamps, the proof that a certificate was logged, with -ext ct_precert_scts. On github.com today that showed two SCTs from different logs, both stamped Oct 30 00:21:07 2026 GMT, minutes after the certificate's notBefore. Chrome and Safari require SCTs for publicly trusted certificates, which is why logging is effectively mandatory.

Write the record

For a site that only uses Let's Encrypt and never needs wildcards:

example.com.  3600  IN  CAA  0 issue "letsencrypt.org"
example.com.  3600  IN  CAA  0 issuewild ";"
example.com.  3600  IN  CAA  0 iodef "mailto:[email protected]"

To lock issuance to your own ACME account and validation method, Let's Encrypt supports the RFC 8657 parameters:

example.com.  CAA  0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/123456789; validationmethods=dns-01"

That closes the dangling-subdomain scenario: an attacker who can answer HTTP challenges on a forgotten subdomain still cannot use their own account or http-01. Find your account URI in your ACME client's account files (Certbot keeps it under /etc/letsencrypt/accounts/).

Before you publish, list every system that issues certificates for you: your CDN, load balancer, mail server, any SaaS custom-domain feature. A CDN that issues on your behalf needs its CAs in the record, or its renewals will fail. That is the most common way CAA causes an outage.

A browser-side verification workflow

  1. Look up the CAA set in the PixelValut DNS Lookup tool. Select CAA; it queries public resolvers directly from your browser.
  2. Pull the issuer history from crt.sh for the apex and for %.yourdomain.com to include subdomains.
  3. Compare: every issuer in the logs should map to a value in issue or issuewild.
  4. Check what the server presents now with the PixelValut SSL Checker, and paste any PEM you are unsure about into the PixelValut Certificate Decoder to read SANs and validity.
  5. Re-check the record from a second resolver after the TTL expires.

For the wider DNS picture, my guide on auditing DNS records before a migration covers the rest of the zone, and subdomain takeover and dangling DNS explains how forgotten records become valid certificates.

Ongoing monitoring

CAA is checked only at issuance, so it is a gate, not an alarm. CT monitoring is the alarm. Free options include Cert Spotter from SSLMate and Meta's Certificate Transparency Monitoring tool, both of which email you when a new certificate for your domain appears. A weekly crt.sh query in CI that diffs against a list of expected issuers also works. Pair that alert with your renewal checks from checking an SSL certificate.

Rollout checklist

  • [ ] Query crt.sh for the apex and %.domain and list every issuer from the last 12 months
  • [ ] List every platform that issues certificates for you (CDN, mail, SaaS custom domains)
  • [ ] Publish issue records for those CAs only
  • [ ] Add issuewild ";" unless you actually use wildcards
  • [ ] Add an iodef contact
  • [ ] Consider accounturi and validationmethods for ACME
  • [ ] Verify from two resolvers after the TTL
  • [ ] Force a test renewal on staging before the next real one
  • [ ] Turn on CT alerts for the domain and look-alike names

Frequently asked questions

Does a CAA record affect browsers or visitors?

No. Browsers never check CAA. Only certificate authorities read it, at the moment they are asked to issue. Existing certificates keep working after you add or change the record.

What happens if I have no CAA record?

Any publicly trusted CA may issue for your domain if the requester passes validation. That is the default for most domains, and the main reason to add one.

Can a CAA record break my certificate renewals?

Yes, if your CDN, hosting provider or mail service uses a CA you did not list. List every issuer from your CT history before publishing, and test a renewal soon after.

Do subdomains need their own CAA records?

Not usually. CAs climb from the requested name to the parent domain and use the first CAA set they find. Add a subdomain-specific set only when it uses a different provider.

Is CT monitoring useful if I already have CAA?

Yes. CAA depends on every CA honoring it. CT shows what was actually issued, including certificates for look-alike names and anything a misbehaving CA produced.

What is the critical flag 128?

It tells a CA that it must understand that tag or refuse to issue. Use 0 for standard tags; 128 is for future tags where ignoring them would be unsafe.

Follow & Connect

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.