Lock down your domain with DNSSEC and CAA to reduce hijacking risk
For developers who control DNS and need a practical hardening checklist, this shows how to publish CAA records, enable DNSSEC, and verify the chain from zone to parent. You will finish with a signed zone, restrictive certificate issuance policy, and concrete checks for the failure modes that cause outages.
TL;DR — To reduce domain hijacking risk, do two things at the DNS layer: enable DNSSEC at both your DNS host and registrar, and publish restrictive CAA records so only your intended CA can issue certificates. The most common failure is enabling DNSSEC at the zone but not publishing the DS record at the parent, which causes
SERVFAILfor validating resolvers. Reading time: ~5 min
Goal
When you are done, your domain has valid DNSSEC from the parent zone to your authoritative nameservers, restrictive CAA records visible in DNS, and verification commands return signed answers instead of SERVFAIL or unsigned responses.
Prerequisites
- Registrar access for the domain and permission to edit delegation / DS records
- DNS hosting provider access and permission to edit zone records and DNSSEC settings
- A domain you control, shown below as
example.com digfrom BIND 9.18+ — check with:
dig -v
delvfrom BIND 9.18+ for DNSSEC validation checks — check with:
delv -v
whoisor RDAP client access to inspect registrar state — check with:
whois example.com
- The CA(s) you actually want to allow, for example
letsencrypt.orgorpki.goog
Steps
Step 1: Inventory the current zone and delegation
Run these commands before changing anything:
domain=example.com
dig +short NS $domain
dig +short DS $domain
dig +dnssec DNSKEY $domain
dig +short CAA $domain
You should see your current nameservers, either no DS yet or an existing one, DNSKEY records if the zone is already signed, and any existing CAA records.
Step 2: Publish restrictive CAA records
Add these records in your DNS provider's dashboard under the zone editor (for example, in your provider's dashboard: DNS/Zone Editor → Add Record):
| Type | Name | TTL | Value |
|---|---|---|---|
| CAA | @ | 300 | 0 issue "letsencrypt.org" |
| CAA | @ | 300 | 0 issuewild "letsencrypt.org" |
| CAA | @ | 300 | 0 iodef "mailto:dns-alerts@example.com" |
If you use a different CA, replace letsencrypt.org with the CA domain they document for CAA. If you intentionally want to block all issuance except one CA, do not add extra issue records.
Success looks like this:
dig +short CAA example.com
0 issue "letsencrypt.org"
0 issuewild "letsencrypt.org"
0 iodef "mailto:dns-alerts@example.com"
Step 3: Enable DNSSEC signing at the DNS host
In your DNS provider's dashboard, open the zone for example.com and turn on DNSSEC signing. The exact label varies, but it is typically one of:
- DNS/Zone Editor → DNSSEC → Enable
- Domain/Zone Settings → DNSSEC → Turn On
After enabling, fetch the DNSKEY set:
dig +dnssec DNSKEY example.com
A successful result includes one or more DNSKEY records and an RRSIG over the DNSKEY RRset, with output shaped like:
example.com. 300 IN DNSKEY 257 3 13 abcdef...
example.com. 300 IN DNSKEY 256 3 13 ghijkl...
example.com. 300 IN RRSIG DNSKEY 13 2 300 20261130000000 20261030000000 12345 example.com. mnopqr...
⚠️ If you publish a DS record at the registrar that does not match the active DNSKEY at the DNS host, validating resolvers will return
SERVFAILfor the whole zone. If this is a production domain, do this during a low-traffic window.
Step 4: Publish the DS record at the registrar
Get the DS record from your DNS provider's DNSSEC page, or derive it locally if you have the KSK and tooling. Most teams will copy the exact DS values shown by the DNS host into the registrar's DS form.
In your registrar's dashboard, open the domain and add the DS record under a path like:
- Domains →
example.com→ DNSSEC / Delegation Signer / DS Records → Add DS Record
Enter the literal values provided by your DNS host, for example:
- Key Tag:
12345 - Algorithm:
13 - Digest Type:
2 - Digest:
A1B2C3D4E5F6...
Then verify the parent publishes it:
dig +short DS example.com
Success is one DS line matching the values you entered, for example:
12345 13 2 A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2
Step 5: Validate DNSSEC end to end
Use a validating resolver and delv:
delv example.com A
dig @1.1.1.1 +dnssec example.com A
dig @8.8.8.8 +dnssec example.com A
Success means delv returns the answer without a validation error, and dig responses include the ad flag when your local resolver validates, or at minimum include RRSIG records in the answer section.
A healthy dig shape looks like:
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2
;; ANSWER SECTION:
example.com. 300 IN A 203.0.113.10
example.com. 300 IN RRSIG A 13 2 300 20261130000000 20261030000000 12345 example.com. abcdef...
A broken DS/DNSKEY chain usually looks like this:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 41234
;; flags: qr rd ra; QUERY: 1, ANSWER: 0
Step 6: Confirm CAA is enforced the way you expect
Query the exact owner name and a subdomain you issue from:
dig +short CAA example.com
dig +short CAA www.example.com
If www.example.com has no explicit CAA, that is fine; CAs climb the tree and use the parent example.com CAA. Success is seeing your intended CA only, plus optional iodef.
Verify it works
Run this set of checks:
domain=example.com
dig +short DS $domain
dig +dnssec DNSKEY $domain
delv $domain A
dig +short CAA $domain
Expected end state:
dig +short DS example.comreturns one or more DS recordsdig +dnssec DNSKEY example.comreturnsDNSKEYplusRRSIGdelv example.com Areturns an answer, notbroken trust chainorresolution failed: SERVFAILdig +short CAA example.comreturns only the CA domains you intend to authorize
If you also want to prove certificate issuance policy is aligned with DNS, inspect the live certificate issuer after renewal:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject
You should see an issuer from the CA you allowed in CAA.
Common pitfalls
DS record added before DNSKEY is live
Mistake: publishing the DS at the registrar before the DNS host has active signing keys.
Symptom: validating resolvers return:
status: SERVFAIL
Fix: remove the DS record at the registrar, wait for parent TTL expiry, enable signing correctly, then re-add the matching DS.
Wrong digest or algorithm in the DS form
Mistake: copying the wrong DS values, or selecting a different algorithm/digest type than the DNS host generated.
Symptom: delv example.com A reports a broken trust chain or no valid DNSKEY/DS match.
Fix: delete the DS entry and paste the exact Key Tag, Algorithm, Digest Type, and Digest from the DNS host's DNSSEC page.
CAA authorizes the wrong CA domain
Mistake: using a human brand name instead of the CA's documented CAA domain.
Symptom: certificate issuance or renewal fails at your CA with a CAA rejection.
Fix: replace the CAA value with the CA's documented domain, for example letsencrypt.org, then verify with:
dig +short CAA example.com
Expecting immediate propagation after registrar DS changes
Mistake: testing seconds after adding or removing DS at the parent.
Symptom: one resolver works while another still returns old state or SERVFAIL.
Fix: wait at least the parent zone TTL and re-test against multiple resolvers:
dig @1.1.1.1 +short DS example.com
dig @8.8.8.8 +short DS example.com
Publishing CAA only on a subdomain while issuing from the apex
Mistake: adding CAA on www.example.com but requesting certificates for example.com too.
Symptom: www issuance works, apex issuance fails or uses different inherited policy.
Fix: publish the same restrictive CAA at @ for the apex, then re-check both names.
Local resolver cache hides the current state
Mistake: trusting one cached answer after changing DS, DNSKEY, or CAA.
Symptom: your laptop shows stale records while public resolvers show the new ones.
Fix: query authoritative or public resolvers directly:
dig @1.1.1.1 +dnssec example.com A
dig @8.8.8.8 +short CAA example.com
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI