A domain that never sends email can still appear in a forged From line, and a domain that never receives email still gets delivery attempts. Three DNS records tell the world both facts: a null MX so senders stop trying to deliver, an SPF record that authorizes no one, and a DMARC policy asking receivers to reject anything that claims to come from you. The details that matter are subdomains, reporting, and making sure the domain really handles no mail at all.
The three records and what each one stops
| Record | Name | Value | What it stops |
|---|---|---|---|
| Null MX | example.com. |
MX 0 . |
Delivery attempts. Senders fail at once instead of retrying for days |
| SPF | example.com. |
TXT "v=spf1 -all" |
Any sending address passing SPF for the envelope sender or HELO name |
| DMARC | _dmarc.example.com. |
TXT "v=DMARC1; p=reject; sp=reject" |
Forged From addresses at the domain and its subdomains, at receivers that apply DMARC |
Null MX (RFC 7505). The record is a single MX with preference 0 and a bare dot as the exchange, and a domain that publishes it must not publish any other MX. It fixes a quirk of SMTP: if a name has no MX at all, senders fall back to its A or AAAA addresses and, finding no mail server there, keep retrying for a long period, typically a week, according to RFC 7505. With a null MX, a sending server should reject the recipient immediately with reply code 556 and enhanced status 5.1.10. The RFC also warns against the reverse mistake: domains used as envelope senders or in From addresses should not publish a null MX, because receivers may reject their mail with 550 5.7.27.
SPF (RFC 7208). Section 10.1.2 gives v=spf1 -all as the record for a domain that sends no mail. A name may carry only one SPF record; two v=spf1 records make the SPF result a permerror. Other TXT records, such as site-verification tokens, can sit beside it.
DMARC (RFC 9989). Without an SPF-authorized address or a published DKIM key, no message can produce an aligned pass, so every message using the domain in its From line fails DMARC. p=reject asks receivers to refuse those messages, and sp=reject extends that to subdomains. RFC 9989 obsoleted RFC 7489 in May 2026: p, sp, and rua work as before, np and t are new, and pct is gone.
CISA’s advice for federal domains that send no mail adds a useful order of operations: publish p=none with a reporting address first, confirm from the reports that nothing legitimate sends as the domain, then move to p=reject. A freshly registered defensive domain that has never been used has nothing to discover, so going straight to reject is reasonable there.
What example.com publishes
Live capture, 26 September 2026. IANA’s reserved example.com, queried through Cloudflare’s resolver from our test workstation in South Korea (dig’s banner lines trimmed):
$ dig @1.1.1.1 example.com MX +noall +answer
;; (trimmed)
example.com. 300 IN MX 0 .
$ dig @1.1.1.1 example.com TXT +noall +answer
;; (trimmed)
example.com. 300 IN TXT "v=spf1 -all"
example.com. 300 IN TXT "_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9"
$ dig @1.1.1.1 _dmarc.example.com TXT +noall +answer
;; (trimmed)
_dmarc.example.com. 300 IN TXT "v=DMARC1;p=reject;sp=reject;adkim=s;aspf=s"
All three records are there. The second TXT record is an unrelated token, which is fine because only one record starts with v=spf1. The DMARC record also sets strict alignment (adkim=s; aspf=s); nothing can pass for this domain anyway, so strictness only matters as a statement of intent. example.net and example.org returned the same MX, SPF, and DMARC values in the same session.
IANA goes one step further on DKIM.
Live capture, 26 September 2026. A selector name we made up, then the wildcard that answered it (banner lines trimmed):
$ dig @1.1.1.1 andguide-k7q2._domainkey.example.com TXT +noall +answer
;; (trimmed)
andguide-k7q2._domainkey.example.com. 300 IN TXT "v=DKIM1; p="
$ dig @1.1.1.1 '*._domainkey.example.com' TXT +noall +answer
;; (trimmed)
*._domainkey.example.com. 300 IN TXT "v=DKIM1; p="
The made-up selector returned a key record with an empty p=, which RFC 6376 defines as a revoked key, and the wildcard gives the same answer for any selector without its own record. Whether you need this is a question the sources answer differently; see the DKIM section below.
Live capture, 26 September 2026. The same zone one level down, at www:
$ dig @1.1.1.1 www.example.com A +noall +answer +comments | grep -E 'status|IN'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51380
www.example.com. 147 IN A 172.66.147.243
www.example.com. 147 IN A 104.20.23.154
$ dig @1.1.1.1 www.example.com MX +noall +answer +comments | grep -E 'status|IN'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30038
$ dig @1.1.1.1 www.example.com TXT +noall +answer +comments | grep -E 'status|IN'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51990
www.example.com has addresses but no MX and no SPF record of its own. That shows exactly how far each apex record reaches. The apex’s sp=reject covers forged From addresses at www through DMARC’s fallback to the parent policy, but the null MX and SPF record stop at the apex, and a sender following the implicit-MX rule would try www’s addresses.
Records to copy
For a parked domain with no website:
; Parked domain: no website, no mail
example.com. 3600 IN MX 0 .
example.com. 3600 IN TXT "v=spf1 -all"
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s"
For a web-only domain, repeat MX and SPF on every name that has records, and cover the rest with wildcards. This combines M3AAWG’s parked-domain examples with RFC 7208’s rule that wildcard SPF declarations must be repeated for any host that has records:
; Web-only domain: a website, no mail
example.com. 3600 IN A 192.0.2.10
example.com. 3600 IN MX 0 .
example.com. 3600 IN TXT "v=spf1 -all"
www.example.com. 3600 IN A 192.0.2.10
www.example.com. 3600 IN MX 0 .
www.example.com. 3600 IN TXT "v=spf1 -all"
*.example.com. 3600 IN MX 0 .
*.example.com. 3600 IN TXT "v=spf1 -all"
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s"
RFC 7208 discourages wildcard SPF records and, where you use them anyway, requires the explicit per-name copies shown for www. Skip the wildcard lines if your DNS provider cannot publish wildcards, and rely on the per-name records plus DMARC.
Reports are optional for a domain that should have no mail at all. M3AAWG treats rua as optional for parked domains and notes that it must point at a domain that does receive mail. When the mailbox is on another domain, RFC 9990 requires that domain to authorize the reports:
; In example.com's zone: send aggregate reports elsewhere
_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc@example.net"
; In example.net's zone: accept reports about example.com
example.com._report._dmarc.example.net. 3600 IN TXT "v=DMARC1"
If you hold several parked domains, M3AAWG shows a shortcut: CNAME each _dmarc name to one shared policy record, such as _dmarc.example.org. CNAME _dmarc.parked.example.net., so one edit changes them all. The NCSC acknowledges that some DNS interfaces will not accept every one of these records and advises publishing as many as you can.
Subdomains: what inherits and what does not
| Mechanism | Covers subdomains automatically? | Why |
|---|---|---|
| MX and null MX | No | Looked up at the exact name; a name with addresses and no MX becomes an implicit mail destination |
| SPF | No | Evaluated for the exact envelope-sender or HELO domain |
| DMARC | Yes, by fallback | A subdomain without its own _dmarc record gets the parent’s sp (or np for names that do not exist), falling back to p |
| DKIM | Not applicable | Keys are looked up per selector under the signing domain |
CISA’s guidance leans on that DMARC fallback: once the parent is at p=reject, it calls SPF null records on every active name unnecessary, though not harmful. That covers forged From addresses; it does nothing about delivery attempts to names with addresses, which only a null MX stops.
A subdomain that does send mail can publish its own _dmarc record. RFC 9989 applies the record found at the author domain before looking at any parent, so it overrides the parent’s sp for that name.
Why np= may never apply
RFC 9989 imported np from RFC 9091, and it applies only to non-existent subdomains, which the RFC defines strictly: the query must return NXDOMAIN. Two setups hide NXDOMAIN from receivers. A wildcard record makes every name exist. Compact denial of existence, used by some DNSSEC-signed zones, answers NOERROR to DNSSEC-aware resolvers.
Live capture, 26 September 2026. A random label under example.com, asked of its authoritative server without and with DNSSEC, then of two public resolvers (the RRSIG line trimmed):
$ dig @hera.ns.cloudflare.com andguide-np-test-7q.example.com A +noall +comments | grep status
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 34486
$ dig @hera.ns.cloudflare.com andguide-np-test-7q.example.com A +dnssec +noall +comments +authority | grep -E 'status|NSEC'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7900
andguide-np-test-7q.example.com. 1800 IN NSEC \000.andguide-np-test-7q.example.com. RRSIG NSEC TYPE128
...
$ dig @1.1.1.1 andguide-np-test-7q.example.com A +noall +comments | grep status
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55042
$ dig @8.8.8.8 andguide-np-test-7q.example.com A +noall +comments | grep status
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13471
Without DNSSEC, the authority says NXDOMAIN. With DNSSEC it answers NOERROR plus an NSEC record whose type list includes TYPE128, the NXNAME marker from RFC 9824. RFC 9824 notes that validating resolvers set the DNSSEC flag on their own queries, so they get the NOERROR form, and both public resolvers passed NOERROR on to us. A mail receiver that judges existence by the response code, as RFC 9989’s definition does, would treat this name as existing through either resolver and apply sp, not np. Set sp to the policy you want for names that don’t exist, and add np with the same value if you like. The DNSSEC guide covers what signing changes for the rest of your zone.
The DKIM question: empty key or nothing
The sources disagree on this, so it helps to see why the disagreement doesn’t matter much:
- NCSC (2019, reviewed 2025) recommends
*._domainkey TXT "v=DKIM1; p=". It calls the record not strictly required, but says some receivers may treat a null key with extra caution and that it revokes any cached keys. - M3AAWG (June 2022) says a parked domain needs no DKIM record and should publish none: with no key in DNS, any forged signature fails anyway.
- IANA publishes the wildcard empty key on example.com, as captured above.
RFC 6376 settles the practical question: an empty p= means the key is revoked, and there is “no defined semantic difference” between a revoked key and a removed one. Either way a forged signature fails, and DMARC’s p=reject does the enforcing. Publish the wildcard if you follow NCSC-based guidance; skip it if your DNS provider handles wildcards awkwardly. It is not the record that protects you.
Our own domain receives mail, so this recipe does not fit it
Live capture, 26 September 2026. The mail records on and.guide (banner lines trimmed):
$ dig @1.1.1.1 and.guide MX +noall +answer
;; (trimmed)
and.guide. 300 IN MX 10 mail.and.guide.
$ dig @1.1.1.1 and.guide TXT +noall +answer
;; (trimmed)
and.guide. 300 IN TXT "google-site-verification=k-o_RnWJ-t3cAi4L1txLBkWSsf3yOGa7Y7fZ9yC5cfM"
and.guide. 300 IN TXT "v=spf1 +a +mx -all"
$ dig @1.1.1.1 _dmarc.and.guide TXT +noall +answer +comments | grep -E 'status|IN'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 127
_dmarc.and.guide. 300 IN TXT "v=DMARC1;p=quarantine;rua=mailto:admin@and.guide"
The MX points at our mail host, which receives mail for admin@and.guide, the contact address on our policy pages. A null MX here would bounce every message sent to us, and RFC 7505 advises against it for any domain used in From addresses. So the apex keeps its real MX, and the three-record recipe applies only to names that handle no mail.
Two details in these records are worth understanding:
+afollows the website, not the mail. Theamechanism authorizes whatever the apex resolves to, and our apex resolves to Cloudflare Pages addresses that serve the website. The mail host is covered bymx. If you copy a defaulta mxrecord, check whatapoints at after every hosting change. (The double space before-allis legal: SPF terms are separated by one or more spaces.)- No
sp=, so subdomains inheritquarantine. The reports go to a mailbox on the same domain, so no RFC 9990 authorization record is needed.
The subdomains are where a non-sending setup would apply, and where our zone shows why you check before you change anything. The zone has a wildcard pointing at our self-hosted deployment server for our own apps.
Live capture, 26 September 2026. Record types at the wildcard owner on the authoritative server, then SMTP reachability at the wildcard’s address and at our real MX:
$ dig @roan.ns.cloudflare.com '*.and.guide' A +noall +answer +comments | grep -E 'status|IN'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21579
;; WARNING: recursion requested but not available
*.and.guide. 300 IN A 115.68.101.36
$ dig @roan.ns.cloudflare.com '*.and.guide' MX +noall +answer +comments | grep -E 'status|IN'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23489
;; WARNING: recursion requested but not available
$ dig @roan.ns.cloudflare.com '*.and.guide' TXT +noall +answer +comments | grep -E 'status|IN'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14845
;; WARNING: recursion requested but not available
$ nc -vz -w 5 audit-probe-7k3m.and.guide 25
nc: connectx to audit-probe-7k3m.and.guide port 25 (tcp) failed: Connection refused
$ nc -vz -w 5 mail.and.guide 25
Connection to mail.and.guide port 25 [tcp/smtp] succeeded!
Our wildcard owns an A record and nothing else. (The WARNING lines match the grep only because “WARNING” contains “IN”.) In the same session, MX, TXT, and _dmarc TXT queries for a random name under and.guide all came back NOERROR with empty answers: such names get an address but no MX and no SPF record, and DMARC falls back to the apex’s quarantine. Under the implicit-MX rule, mail addressed to such a name would go to the deployment server’s address, where port 25 refused our connection while the real MX accepted one from the same workstation. That is the situation RFC 7505 describes: no listener, so a sender queues the message and retries until it gives up.
A wildcard null MX and v=spf1 -all would give those names the non-sending treatment, but we have not added them. RFC 7505 warns against a null MX on any name used to send mail, and DNS cannot tell us whether an app on the deployment server sends mail under its own hostname. The DMARC aggregate reports requested by our rua tag can answer that: if they show no legitimate mail from subdomains, sp=reject and the wildcard records become easy decisions. The dangling DNS audit covers the same wildcard from the web side.
Verify after publishing
Check each name from outside, and compare two resolvers with the DNS lookup tool while old answers are still cached:
dig +short example.com MX # expect: 0 .
dig +short example.com TXT | grep -c 'v=spf1' # expect: 1 (two records = permerror)
dig +short example.com TXT | grep 'v=spf1' # expect: "v=spf1 -all"
dig +short _dmarc.example.com TXT # expect: one v=DMARC1 record
dig +short www.example.com MX # every name with an address: 0 .
dig +short no-such-name.example.com MX # with a wildcard null MX: 0 .
Repeat the checks whenever you add a hostname. Each new name with an address is a new place mail can be sent until it gets its own null MX, because a wildcard never covers a name that has records of its own; the wildcard DNS guide explains where that boundary sits.