MX, SPF, DKIM, DMARC: the four mail records
What it holds#
| Record | Lives at | What it answers |
|---|---|---|
| MX | example.com (apex, or any name) | Where inbound mail for @example.com goes |
| SPF (a TXT) | example.com (apex) | Which IPs are authorized to send as @example.com |
| DKIM (a TXT) | <selector>._domainkey.example.com | The public key that verifies signatures on outbound mail |
| DMARC (a TXT) | _dmarc.example.com | What a receiver does when no SPF or DKIM pass aligns with the visible From: (RFC 9989 §4.1) |
Each one without the others is brittle.
Defined in RFC 1035 (MX, sender behavior in RFC 5321 §5.1), RFC 7208 (SPF), RFC 6376 (DKIM), and RFC 9989 (DMARC, obsoletes RFC 7489).
When to use it#
- The domain receives mail: publish MX.
- The domain sends mail: publish SPF, DKIM, and DMARC so receivers can tell your mail from forgeries.
- The domain never touches mail: publish a null MX (
MX 0 ., RFC 7505), an SPF ofv=spf1 -all, and a DMARC ofp=reject. A registered domain with no mail records is still a forgery target.
When not to use it#
- No DKIM key on a receive-only domain. There is no outbound mail to sign, so there is no key to publish.
- Never a second SPF record at the same name. Two SPF TXT records is a
permerror(RFC 7208 §4.5); merge the mechanisms into one record. - Not
p=rejecton day one. Publishp=nonewithrua=set, read the aggregate reports until alignment is confirmed, then promote.
dig example#
Asks a domain apex for its published text, which is where SPF lives.
dig @1.1.1.1 example.com TXTGO NOERROR and a string starting v=spf1
NO GO no v=spf1 string, which means the domain publishes no SPF record. DMARC is a separate lookup at _dmarc
MX, SPF, and DMARC can all be checked blind in one paste:
dig @1.1.1.1 cloudflare.com MX +short
dig @1.1.1.1 cloudflare.com TXT +short | grep v=spf1
dig @1.1.1.1 _dmarc.cloudflare.com TXT +short5 mxa-canary.global.inbound.cf-emailsecurity.net.
5 mxb-canary.global.inbound.cf-emailsecurity.net.
10 mxa.global.inbound.cf-emailsecurity.net.
10 mxb.global.inbound.cf-emailsecurity.net.
"v=spf1 ip4:199.15.212.0/22 ip4:173.245.48.0/20 include:_spf.google.com include:spf1.mcsv.net include:spf.mandrillapp.com include:mail.zendesk.com include:stspg-customer.com include:_spf.salesforce.com -all"
"v=DMARC1; p=reject; sp=reject; adkim=r; aspf=r; pct=100; rua=mailto:<token>@dmarc-reports.cloudflare.net,mailto:rua@cloudflare.com"An empty answer on the _dmarc. line means no DMARC record is published. The MX preference number, not the answer order, decides which server a sender tries first.
DKIM needs the selector from a received message's DKIM-Signature: header, so it cannot be queried blind:
dig @1.1.1.1 protonmail._domainkey.proton.me TXT +shortprotonmail.domainkey.drfeyjwh4gwlal4e2rhajsytrp6auv2nhenecpzigu7muak6lw6ya.domains.proton.ch.
"v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn46zCm3zHBmS
1zePKxA+RIXw41Nu6l91NpLLBnWnrcZ35H/843XNWPZEQ0OgGwx/yqTXETMLXIDjGEWlK1E1
mpdguqu+3s7SuIHoo5+i6mgyxJguljkwc3dk8ojnJ6VVUPnDh5GJArkAhXxEb1aOK1BVGM0y
DlmYdmaOfd48qcx5iODP/MFc8pivfxEXTIL+aUz"
"7+X69lMiwUSHpWYL3/a5X3nLD0zEntxv08xs8J/rpuRg4v+OXEOhcNvhkeiRZqJBdpJTkoEZ
fGvdTct+U0YYC69NW0ClUcKio2uDPmxU1xvfvHbSTW2gHYk8RpYZaxLACULdMo+Vt4Na/oIR
+swIDAQAB;"The first line is a CNAME into Proton's own zone, so Proton can rotate the key without touching proton.me. The key exceeds the 255-byte TXT string limit and is split into two quoted strings, concatenated before use (RFC 6376 §3.6.2.2).
Gotchas#
- MX targets must be A/AAAA, never a CNAME (RFC 2181 §10.3).
- No MX does not stop mail. Senders fall back to an implicit MX at the domain's A/AAAA (RFC 5321 §5.1); publish a null MX (RFC 7505) if the domain sends no mail at all.
- SPF checks the envelope
MAIL FROM, not the visibleFrom:header. A bounce with an emptyMAIL FROMis checked aspostmaster@<HELO hostname>instead (RFC 7208 §2.4). - SPF caps DNS lookups at 10 per evaluation;
include:sprawl past the cap is apermerror(RFC 7208 §4.6.4). - An empty DKIM
p=is a revoked key, not a broken record (RFC 6376 §3.6.1). - DMARC covers your exact domain and its subdomains only. Lookalike domains (
examp1e.com) are outside its reach. pct=is removed from the current DMARC spec (RFC 9989 Appendix A.6); records in the wild still carry it.