Most certificate security advice is about the cert itself: key length, chain validity, expiry. CAA is the one control that works a layer up, on the issuance decision. It won't make any single certificate stronger. What it does is decide who's allowed to mint a certificate for your domain at all. Run a fleet of names across a few CAs without ever publishing a CAA record, and right now any public CA on earth can issue for you — and you'd find out from a Certificate Transparency alert, if you happen to be watching.
So let's fix that. Here's what CAA does, what it can't do, the RFC 8657 binding almost everyone skips, and how to ship it without locking yourself out of your own renewals.
What a CAA record actually enforces
CAA — Certification Authority Authorization, RFC 8659 — is a DNS record type that names which certificate authorities may issue for your domain. When a CA gets an issuance request, the Baseline Requirements make it look up the CAA record set for the requested name and refuse if its own identifier isn't in there. Mandatory for publicly-trusted CAs since September 2017, and it has teeth: a CA that issues against a CAA record has committed a misissuance and has to revoke.
That word "issuance" is the whole point, and also the whole limitation. CAA gets checked exactly once, the moment a CA decides whether to sign. It's a publication control that CAs enforce out of band. Nothing at the TLS layer touches it. No browser, no client, no server looks at CAA during a handshake. Nothing in the actual connection path cares.
Here's the consequence that trips people up. A certificate issued before you published a restrictive CAA record keeps working right up to its expiry. Publishing a record doesn't reach back and invalidate anything. If some CA you don't trust issued a cert for your domain last month, your shiny new record does exactly nothing to it. You've only blocked the next attempt. CAA is a forward-looking lock on the door — it stops the next person from walking in, but whoever's already inside stays inside.
Which is the whole reason CAA on its own is incomplete, and why CT monitoring shows up again at the end.
The three property tags, and why issuewild earns its keep
A CAA record set is built from property tags. Three of them matter.
issue authorizes a CA to issue certificates of any kind for the domain. You set it to the CA's CAA identifier domain: letsencrypt.org, amazon.com for ACM, digicert.com, pki.goog for Google, sectigo.com. The value is a string the CA publishes, so check their docs — it isn't always the domain you'd guess. An issue value of a single semicolon means "no CA may issue." A deliberate deny-all.
issuewild authorizes issuance specifically for wildcard certs, and it's the tag people forget exists. The rule worth memorizing: if you publish an issuewild record, it beats issue for wildcard requests. Publish only issue records and they cover wildcards too. The leverage is lopsided, and that's useful. Most platform teams are wildcard-heavy — nobody wants a fresh cert per ephemeral subdomain — so issuewild lets you say "this CA can issue specific names, but only that other CA gets to produce *.prod.example.com." A wildcard is the higher-blast-radius cert. One leaked wildcard private key burns every name under it. Restricting which CA can even make one is worth doing on its own merits.
iodef is the reporting tag. It names a mailto or URL where a CA should send a report when a request violates your policy. I'll be honest about iodef down in the failure-modes section.
Tree-climbing, and where it bites subdomains
CAA resolution walks up the DNS tree. Checking issuance for api.staging.example.com, a CA looks for a record set at that exact name first. Nothing there? It drops the leftmost label and tries staging.example.com, then example.com, climbing until it hits a record set or runs out of domain. First node with any CAA record wins, and the search stops dead.
Convenient, mostly: one record at the apex covers everything below it. It's also the single most common way teams shoot themselves in the foot. Because the first record set found wins and the climb halts there, a record on a parent governs every subdomain that doesn't carry its own. Lock example.com to one CA while some team three levels down is quietly renewing through a different CA via their own ACME setup, and their renewals die the instant your apex record goes live. They won't link it to your DNS change either, because the failure surfaces as a CA-side validation error, not a DNS error. Good luck to them.
Want a subtree on a different CA? Publish a record set at that subtree node. It overrides the apex completely for everything underneath — including dropping any apex CAs you forgot to re-list. The override is total, not additive. Whatever sits at the closer node is the entire policy; the parent's records don't merge in. Miss one and you've just locked out a CA you meant to keep.
RFC 8657: binding issuance to an account and a method
Plain CAA says "Let's Encrypt may issue for me." Good. But anyone holding a Let's Encrypt account plus control of your DNS or HTTP validation path can still pull a cert. That includes an attacker sitting on a compromised DNS provider API token. It includes another team in your own org running an ACME account you've never heard of. CAA at the CA-identifier level can't tell your Let's Encrypt account apart from someone else's.
RFC 8657 closes the gap with two parameters you bolt onto an issue or issuewild record.
accounturi pins issuance to one specific ACME account URI — the URI your CA handed you at registration. Only requests authenticated by that exact account get honored. It lives in your ACME client's account data: certbot keeps it under its accounts directory, acme.sh and lego store it in their account registration. Once it's in your CAA record, a leaked DNS token by itself is no longer enough; an attacker also needs your ACME account key to forge a request the CA will accept. You've upgraded the bar from "control of DNS" to "control of DNS and the specific account key." Meaningfully harder.
validationmethods restricts which ACME challenge types pass: dns-01, http-01, tls-alpn-01. If all your automation runs DNS-01, pin it to dns-01. Now an attacker who briefly hijacks HTTP on one of your hosts can't ride an http-01 challenge to a cert, because the CA rejects any method you didn't list. You've collapsed the set of abusable validation paths down to the one you actually use.
Put together, accounturi and validationmethods turn CAA from "which CA" into "which account, using which method, at which CA." That's the version worth aiming for. Support is solid across the major ACME CAs — Let's Encrypt and Google's ACME endpoint both honor it — but confirm your CA implements 8657 before you lean on it. A CA that ignores the parameters will cheerfully issue as if you'd never written them.
The operational failure modes
CAA is one of those controls where the thing that breaks is you, locking yourself out. Walk in with your eyes open.
CA migration is the big one. You decide to move off DigiCert onto Let's Encrypt. New ACME automation, works in a test, you cut over — and renewals start failing across the estate. Cause: your apex CAA record still lists only digicert.com, so the new Let's Encrypt account is getting bounced at the CAA check. Obvious once you see it. Add the new CA's identifier before you migrate, run both through an overlap window, and pull the old one only after the last cert from the old CA has rotated out. CAA changes lead a migration. They never trail it.
Low TTLs matter more than they used to. In the old 398-day world you touched CAA roughly never. Under SC-081v3 lifetimes are falling off a cliff — the 200-day phase is already live as of March 2026, 100 days lands March 2027, 47 days in 2029. More renewals means more issuance events, more moments where a stale record can wall you off, more times you're swapping a CA under the clock. Keep CAA TTLs low, 300 to 3600 seconds, so that when you add a CA to unstick a renewal the change propagates in minutes instead of however many hours your DNS provider defaults to. A high TTL on a CAA record is an outage you scheduled for yourself on the exact day you need to change it fast.
DNSSEC is worth a thought too. CAA records get signed like anything else in a DNSSEC-signed zone, and CAs validate DNSSEC when it's present. So if your signing is broken — expired RRSIG, a key rollover gone sideways — the CA may fail to resolve CAA and refuse to issue. A DNSSEC problem you might otherwise have ignored for weeks turns into an immediate renewal blocker. Keep that in your back pocket the next time a renewal fails for no reason you can find.
Then there's iodef, which nobody reads. It tells CAs where to report policy violations. In practice reporting is implemented all over the map, and when a report does land it lands in some shared inbox nobody's looking at. Setting iodef is fine. Believing it's an alerting mechanism is the mistake. It is not your detection control. Treat it as a courtesy channel, not a tripwire.
CAA is the lock. CT logs are the camera.
The honest limit of everything above: CAA tells compliant CAs to refuse unauthorized issuance. It does nothing against a CA that ignores the rules, a CA that's been compromised, or the gap CAA structurally can't cover — a cert issued before your record existed. CAA stops the issuance. It can't tell you someone tried, and it can't show you what's already out there.
That's Certificate Transparency's job. Every publicly-trusted certificate gets logged to CT, and watching those logs for your domains tells you when a cert shows up that you weren't expecting: a name issued through a CA your record doesn't authorize (so either a misissuance or a record that's wrong), a typosquat riding your brand, or a cert minted in the window before your lock went up. CAA and CT cover different halves. The lock stops the routine unauthorized issuance; the camera tells you when something got through or somebody rattled the handle. This is exactly the pairing CertPulse is built on — CAA records you manage in DNS, CertPulse watching CT logs through CertStream, so the certs that slip past the lock don't slip past you.
A rollout that won't lock you out
Don't jump from nothing to account-pinned in one move. Stage it.
Start by auditing who actually issues for you. Pull your CT history and list every CA that's issued a live certificate across your domains. Reconcile that against what you think you run, because there's almost always something — a forgotten ACM cert, a vendor issuing on your behalf, a legacy CA still auto-renewing in a corner. You can't write a correct CAA record until you know the real issuer set.
Then publish permissive records naming every CA from that audit, at the apex, low TTLs. This shuts out the entire rest of the CA ecosystem while breaking none of your own renewals. Let it sit through a renewal cycle and watch for failures. Something breaks, you missed an issuer — add it.
Then tighten. Add issuewild restrictions wherever wildcards should come from a narrower set than your regular certs. Layer in accounturi and validationmethods on the domains whose automation is stable enough to pin. Do the high-value names first — the wildcards, the apex, anything customer-facing — and leave the chaotic experimental subdomains permissive until their issuance settles down.
Done this way every step reverses cleanly and every breakage points straight at a specific missing entry. CAA is a small DNS change with a security payoff way out of proportion to the effort, and the only real risk in deploying it is moving faster than your own knowledge of who issues for you. Audit first. Writing the records is the easy part.
This is why we built CertPulse
CertPulse connects to your AWS, Azure, and GCP accounts, enumerates every certificate, monitors your external endpoints, and watches Certificate Transparency logs. One dashboard for every cert. Alerts when auto-renewal fails. Alerts when certs approach expiry. Alerts when someone issues a cert for your domain that you didn't request.
If you're looking for complete certificate visibility without maintaining scripts, we can get you there in about 5 minutes.