CAA Records: Telling Certificate Authorities Who May Issue for Your Domain
How DNS Certification Authority Authorization records work under RFC 8659, what the issue, issuewild, and iodef tags mean, and how to publish and verify them without locking out your own renewals.
Any publicly trusted certificate authority can issue a certificate for any domain. Yours included. The only thing standing between a stranger and a valid certificate for your name is the CA's domain validation step, and that step has failed in every way you can imagine: a validation method with a hole in it, a hijacked web server, a compromised CA, a convincing support ticket.
CAA, short for Certification Authority Authorization, is the domain owner's lever on that process. It's a DNS record that says "only these CAs may issue for this name", and publicly trusted CAs are required to check it before they issue. It won't make you bulletproof, but it is one of the cheapest controls in TLS, and it's one most domains still don't publish.
This post covers the record format from RFC 8659, how a CA actually looks the record up, what it protects against and what it doesn't, and how to roll it out without breaking your own renewals. That last part is where people get hurt.
The record
CAA is DNS record type 257. Each record has three fields:
| Field | Meaning |
|---|---|
| Flags | An 8-bit field. Only bit 0, the issuer critical flag with value 128, is currently defined. |
| Tag | The property name, such as issue, issuewild, or iodef. |
| Value | The property value, written as a quoted string in zone-file format. |
The simplest useful record looks like this:
example.com. IN CAA 0 issue "letsencrypt.org"
Read it as "for example.com, the CA that identifies itself as letsencrypt.org may issue". Any CA whose identifier isn't listed has to refuse.
Real zones usually have a few. Here's one that uses a commercial CA for production, lets a dev subdomain use Let's Encrypt, and gives CAs an address to report refused requests to. Notice the subdomain record: it replaces the apex policy for dev.example.com, it doesn't add to it. More on that below.
example.com. IN CAA 0 issue "digicert.com"
dev.example.com. IN CAA 0 issue "letsencrypt.org"
example.com. IN CAA 0 iodef "mailto:security@example.com"
The three tags
RFC 8659 defines three property tags, matched case-insensitively.
issue
issue allows one CA to issue certificates for the name. The value is the domain the CA publishes as its CAA identifier, optionally followed by parameters separated by semicolons.
example.com. IN CAA 0 issue "ca.example.net"
example.com. IN CAA 0 issue "ca.example.net; account=230123"
The parameters are defined by each CA, not by the RFC. RFC 8657 standardizes two of them for ACME CAs, validationmethods and accounturi, which let you pin issuance to one ACME account or one validation method. Useful, but check your CA actually honours them before you lean on it.
An issue value of just a semicolon means nobody:
example.com. IN CAA 0 issue ";"
That's the record for a domain that should never have a certificate, like one you only use for email.
issuewild
issuewild governs wildcard certificates, meaning any certificate whose first label is *. The rule that catches people out: if there is any issuewild record in the set, it alone decides wildcard issuance and the issue records are ignored for wildcards. If there's no issuewild, the issue records cover wildcards too.
example.com. IN CAA 0 issue "letsencrypt.org"
example.com. IN CAA 0 issuewild ";"
Those two lines allow ordinary certificates from one CA and forbid wildcards from everyone. The common mistake is adding issuewild for a single CA and expecting it to be additive. It isn't. It replaces the issue set for wildcard requests.
iodef
iodef names somewhere to send reports. When a CA refuses a request because of your CAA records, it may send you a report there. The value is a mailto:, http: or https: URL, and the format is IODEF, the Incident Object Description Exchange Format. Support is optional on the CA side.
example.com. IN CAA 0 iodef "mailto:security@example.com"
Don't count on these reports. Many CAs never send them. If you want to know what was actually issued for your names, Certificate Transparency logs are the reliable source.
How a CA finds the record
This is the part worth understanding properly, because it decides which records apply to a given name. For a request naming www.example.com, the CA checks, in order:
- www.example.com
- example.com
- com (the search stops at the first name with CAA records)
It queries the exact name first. If that name has CAA records, that's the answer and the search stops. If not, it strips the leftmost label and tries again, all the way up to the root. The first non-empty set wins. So a record at the apex covers every subdomain that doesn't publish its own.
Two things fall out of that:
- A subdomain record overrides the parent. If
dev.example.comlists onlyletsencrypt.org, DigiCert can't issue fordev.example.comeven though the apex allows it. - No records anywhere means every CA may issue. CAA is opt-in. Silence is permission.
RFC 8659 replaced the original CAA spec, RFC 6844, and the biggest change was right here. The old algorithm followed CNAME and DNAME aliases during the climb, which produced some genuinely confusing results. Under the new one the queried name is resolved normally, so a CNAME at that name is followed like any DNS client would, but the climb itself walks the plain parent labels. If a CA tells you a CAA lookup failed and you can't see why, this is where to start looking.
The issuer critical flag
Flags value 128 marks a record as critical, meaning a CA that doesn't understand the tag must refuse to issue. Every conforming CA understands the three standard tags, so today the flag mostly matters if a future spec adds a tag you want strictly enforced. A flags-0 record with an unknown tag is just ignored.
Most people publish flags 0. Setting 128 on an issue record is harmless and makes your intent clear, but it doesn't restrict anything further.
Why bother
CAs have been required to check CAA since September 2017 under the CA/Browser Forum Baseline Requirements. A CA that issues against a published CAA record has misissued, and is expected to revoke the certificate and report the incident. In other words, your record is enforced by policy across the whole public PKI, not just by a vendor you have a contract with.
That gives you a few things for the price of one DNS record. If a validation flaw or an operational mistake lets someone get a certificate from a CA you never use, a CAA record listing only your real CAs stops it. The same record stops a well-meaning colleague from clicking the one-click SSL button at a web host and ending up with a certificate from a CA you didn't choose. And it's a public, machine-readable statement of which CAs should appear in your inventory, which makes anything else in CT logs or on your endpoints immediately suspicious.
What it won't do
CAA controls issuance, not trust. Keep that in mind before you lean on it.
- Browsers ignore it. A certificate issued in violation of CAA validates like any other. CAA stops the CA from issuing. It does nothing about a certificate that was issued anyway.
- It's only as strong as your DNS. CAs query DNS over the public internet. Anyone who controls your zone, or can spoof answers to the CA's resolver, can rewrite the record. DNSSEC closes that gap. RFC 8659 says CAs should validate DNSSEC where available, and a signed zone that returns a bogus answer must be treated as a lookup failure rather than as permission.
- It only binds publicly trusted CAs. Your internal enterprise CA isn't subject to the Baseline Requirements and will issue whatever its own policy says.
- It's checked at issuance time. The CA checks CAA when it validates the request, and the Baseline Requirements let it reuse that result for a limited window. Changing the record afterwards doesn't revoke anything.
- It doesn't prove your certificates came from the listed CA. You still have to look at what the endpoints are actually serving.
Publishing it without breaking your renewals
The most common CAA outage is self-inflicted. Someone publishes a strict record, and the next renewal fails because the CA that really issues the certificate isn't on the list. Often it's a managed service renewing on the organisation's behalf through a CA nobody remembered.
So before you publish anything, find out who issues for your names today. Three places to look:
- The issuer on every certificate currently installed on your endpoints.
- Certificate Transparency logs, which show every publicly trusted certificate issued for your names, including the ones a cloud provider or CDN obtained for you without asking.
- The documentation of any managed service that terminates TLS for you. Cloud load balancers, CDNs and email providers frequently get their own certificates and publish the CAA identifier you need to allow.
Then use the CA's documented identifier, not a guess. Identifiers don't always match the brand name, and larger CAs often publish several, one per brand or intermediate they run. Let's Encrypt is letsencrypt.org. For anyone else, the CA's CAA page or its Certification Practice Statement is the authoritative source.
A rollout that won't bite you:
- Publish
issuerecords for every CA your inventory turned up, flags 0. - Add an
iodefrecord so the CAs that do send reports can reach you. - Trigger a test issuance or renewal with each CA and watch for CAA errors in your ACME client or the CA's portal.
- Only then add
issuewild ";"if you don't use wildcards, or per-subdomain restrictions if you want them.
ACME clients report CAA failures explicitly. Let's Encrypt, for one, returns an error naming the record that blocked the request. That's far easier to debug than a renewal that silently stopped happening, which is a good argument for testing on purpose rather than waiting for the next scheduled run.
Checking what's published
dig will show you the records directly:
$ dig example.com CAA +short
0 issue "letsencrypt.org"
0 iodef "mailto:security@example.com"
Query the subdomain too, because the answer may be empty there even though a parent has records. The CA will climb to the parent; dig won't. And if you've just made a change, query the authoritative nameservers rather than your resolver so a cached answer doesn't mislead you.
If you'd rather not open a terminal, the CAA record checker does the same lookup from your browser over DNS-over-HTTPS, climbs the parent names the way a CA does, and turns the result into a plain list of which CAs may issue. The generator on the same page builds a correct record set from a list of CAs, which avoids the quoting mistakes hand-written records tend to attract.
If your DNS provider's UI has no CAA type, check whether it accepts generic TYPE257 records. Providers that can't serve CAA at all are rare now. If you've found one, the fix is to move the zone, or at least delegate the relevant name to a provider that can.
Where monitoring fits
CAA makes "which CA should have issued this?" an answerable question. That's useful on its own, and more useful when you compare the policy against what's actually out there. The comparison has three inputs:
- The published policy: CAA records on the apex and on any subdomains that override it.
- What was issued: Certificate Transparency entries for your names, with the issuing CA of each.
- What's being served: the certificate each endpoint presents in the handshake, and its issuer.
A certificate in CT from a CA that isn't in your CAA records is one of three things: a misissuance, an identifier you forgot to allow, or evidence your record was changed or spoofed. All three deserve a look. CertIntel's CT discovery and endpoint observations give you the second and third inputs; the CAA checker gives you the first for any domain, and the other certificate tools will pull an issuer out of any certificate you have to hand. The record itself is yours to publish and keep honest.
Further reading
- RFC 8659: the current CAA specification, including the lookup algorithm, record syntax, and security considerations.
- RFC 8657: the
validationmethodsandaccounturiparameters for ACME CAs. - RFC 6844: the original, now obsoleted, specification, useful when reading older CA documentation.
- CA/Browser Forum Baseline Requirements: the policy that requires publicly trusted CAs to check CAA.
- Let's Encrypt CAA documentation: an example of a CA publishing its identifier and its handling of CAA errors.