Email deliverability
DNS Deep Dive: How SPF, DMARC, MX, and DNSSEC Fit Together
An end-to-end view of mail-flow DNS — how MX, SPF, DKIM, DMARC, and DNSSEC interact, common misconfigs, and the dig commands to verify every layer.
DNS is one of those topics where everyone has a partial mental model. Mail teams know SPF and DMARC; ops teams know A and MX; security teams know DNSSEC. Few people hold the full picture in their head — how a single inbound or outbound mail flow touches all of these records, in what order, and what happens when one piece is missing or misconfigured.
This is a tour through the DNS records that govern mail flow, deliverability, and authenticity. We will walk an inbound message from arrival to inbox, an outbound message from your ESP to a recipient's mail server, and surface the dig commands and validation tools that let you verify each layer is doing its job. The goal is not to memorize record syntax — it is to understand how the records compose, so you can debug deliverability issues by asking the right questions.
We will lean heavily on dig for verification because it is the universal tool. Every example here works on macOS, Linux, and Windows (with WSL). If you prefer GUI tools, intoDNS.com, dnschecker.org, and CheckFast's Email Checker all do the same checks with prettier output.
The MX record: where mail for your domain actually goes
An MX (Mail Exchanger) record tells sending mail servers where to deliver mail for a given domain. The record contains a priority number and a hostname. Multiple MX records form a list; senders try the lowest priority first and fall back to higher numbers on failure. This is why you often see redundant MX records like 1 mail1.example.com and 5 mail2.example.com — primary plus backup.
Critically, MX records cannot point to A or AAAA records by hostname value if those hostnames are CNAMEs. RFC 2181 §10.3 explicitly forbids this, and modern DNS validators (intoDNS, MXToolbox) flag it as a hard error. The fix is to make sure MX targets point to actual A/AAAA records, never CNAMEs.
If you do not have inbound mail at all (a common case for marketing-only domains), publish a "null MX" record per RFC 7505: 0 . (priority 0, target the literal dot). This explicitly tells senders "this domain does not accept mail" and produces a clean rejection at the protocol level rather than mysterious bounces.
Run dig +short MX example.com to see your current MX records. The output should be one or more priority+hostname pairs. If empty, mail to the domain will fall back to the A record (deprecated behavior — newer mail servers reject) or fail outright.
# Verify MX records for a domain
$ dig +short MX checkfast.io
10 aspmx.l.google.com.
20 alt1.aspmx.l.google.com.
20 alt2.aspmx.l.google.com.
30 alt3.aspmx.l.google.com.
30 alt4.aspmx.l.google.com.
# Verify a domain explicitly does not accept mail (null MX)
$ dig +short MX null-mx-example.com
0 .
# Trace the full DNS resolution path for an MX target
$ dig +trace aspmx.l.google.comSPF: the envelope-sender check
SPF (Sender Policy Framework, RFC 7208) is a TXT record at the apex of your domain that lists every IP or include allowed to send mail with your domain in the envelope-from address. When a receiving mail server gets a message, it does an SPF lookup on the envelope-from domain and verifies the sending IP is allowed.
SPF only checks the envelope-from (the SMTP MAIL FROM command), not the visible From: header that the recipient sees. An attacker can spoof the visible From: while using their own domain in the envelope-from, and SPF will pass for the attacker's domain. This is why SPF alone is not sufficient — DMARC adds the alignment requirement that the envelope and header domains match.
The 10-DNS-lookup limit is the most common SPF problem. Each include:, a, mx, exists, or redirect in the record costs one lookup. Stacking three or four ESPs reliably exceeds the limit and produces PermError, which most receivers treat as a hard auth failure. Solutions: remove unused ESPs, replace include: with literal ip4: and ip6: ranges (zero lookups each), or use a flattening service that publishes a single record covering all your senders.
Run dig +short TXT example.com | grep spf1 to see your SPF record. There should be exactly one record beginning with v=spf1. Two records is a permanent error. If you need to combine multiple sources, merge them into one record by combining their include: directives.
CheckFast Email Checker resolves SPF includes recursively, counts lookups, and validates DKIM selectors live.
Validate every DNS recordDKIM: the cryptographic signature
DKIM (DomainKeys Identified Mail, RFC 6376) signs each outbound message with a private key and publishes the corresponding public key in DNS. Receivers verify the signature against the published key. A valid signature proves the message has not been modified in transit and was signed by someone who controls the d= domain.
DKIM keys live at ._domainkey.. The selector is chosen by the sending service — Google uses google._domainkey, Microsoft uses selector1._domainkey and selector2._domainkey, Mailgun uses domain keys named after the API. A single domain can have many DKIM keys, one per selector, and a single message can have multiple DKIM-Signature headers.
Verification requires knowing the selector ahead of time — DNS does not let you list all _domainkey records under a parent. To find your active selectors, look at the DKIM-Signature header on a real outbound message: the s= parameter is the selector. Run dig +short TXT ._domainkey. to see the public key.
Selector rotation is best practice (rotate annually) but easily broken — many ESPs require manual rotation, which means stale 1024-bit keys are still common. Modern receivers prefer 2048-bit keys; some flag 1024-bit as weak. Check your selector's key length and rotate if needed.
# Find a domain's DKIM selectors by inspecting a real DKIM-Signature
# header. Check the s= parameter — that is the selector.
#
# Then resolve the public key:
$ dig +short TXT google._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
# Multiple selectors are common — many ESPs publish two for rotation:
$ dig +short TXT selector1._domainkey.example.com
$ dig +short TXT selector2._domainkey.example.comDMARC: the policy layer that ties SPF and DKIM together
DMARC (Domain-based Message Authentication, Reporting, and Conformance, RFC 7489) is a TXT record at _dmarc.example.com that tells receivers what to do when SPF or DKIM fails to align with the visible From: header. "Alignment" means the domain in the SPF check (envelope-from) or the DKIM signature (d=) matches the From: header domain.
The p= tag sets the policy: none (monitoring only), quarantine (deliver to spam), or reject (bounce). The pct= tag controls what percentage of failing mail receives the policy treatment — used during rollout. The rua= tag specifies where to send aggregate reports; the ruf= tag specifies where to send forensic reports.
DMARC alignment can be "strict" or "relaxed". Strict requires exact domain match (mail.example.com from must match SPF/DKIM domain mail.example.com). Relaxed allows organizational-domain match (mail.example.com aligned with example.com). The adkim= and aspf= tags control alignment mode — r (relaxed, default) or s (strict). Relaxed is correct for almost every real-world deployment.
DMARC subdomain policy is handled by the sp= tag. Without sp=, the policy applies to all subdomains. With sp=, you can have a stricter or looser policy on subdomains — useful for staged rollouts where you want p=reject on the apex but p=none on a transactional subdomain you are still aligning.
# Check a domain's DMARC record
$ dig +short TXT _dmarc.example.com
"v=DMARC1; p=reject; rua=mailto:dmarc@example.com; pct=100; adkim=r; aspf=r;"
# A subdomain inherits the apex policy unless sp= is set
$ dig +short TXT _dmarc.subdomain.example.com
# typically empty — falls back to apex via DMARC tree-walkDNSSEC: signing the answers themselves
DNSSEC (RFC 4033-4035) cryptographically signs DNS responses, letting resolvers verify that the answer they got actually came from the authoritative server and was not tampered with in transit. Without DNSSEC, an on-path attacker can rewrite DNS responses — changing your MX record to point to their mail server, for example, and silently intercepting mail.
DNSSEC adoption has been slow. As of 2026, roughly 5-7% of public domains have DNSSEC enabled. The gap between "important" and "deployed" is large because DNSSEC is operationally heavy: key rotation, KSK ceremonies, signing infrastructure, and the failure mode of "misconfigured DNSSEC takes your domain offline" all push teams away from it.
Modern DNS providers handle the operational burden. Cloudflare, Route 53, and Google Cloud DNS all offer one-click DNSSEC with automatic key rotation. The remaining work is configuring your registrar to publish the DS record (Delegation Signer) that links your domain to the DNSSEC chain. This step is registrar-specific and is the most common point of failure.
DNSSEC matters most for high-value domains: financial services, government, anything where MX or A record manipulation is high-impact. For a typical SaaS, the value is real but smaller than SPF/DKIM/DMARC, because attackers prefer the easier bypass of DMARC misconfigurations to the harder-but-still-possible path of DNS hijacking.
Walking an outbound message through DNS
Take a customer signup confirmation email from noreply@example.com to user@gmail.com. The journey through DNS:
1. Your mail server (or ESP) constructs the message and signs it with your DKIM private key. The DKIM-Signature header includes d=example.com; s=google; and a base64 signature over the message headers and body.
2. The mail server connects to Gmail's MX servers. It queried Gmail's MX records (dig MX gmail.com to find them — 5 gmail-smtp-in.l.google.com. and four backups.
3. The mail server begins SMTP. The MAIL FROM: envelope is bounces@example.com. Gmail performs an SPF check on example.com — dig TXT example.com returns the SPF record, Gmail walks the include chain, and verifies the sending IP is in the allowed list.
4. Gmail receives the DATA. It performs a DKIM verification — dig TXT google._domainkey.example.com to fetch the public key, then verifies the signature on the message.
5. Gmail performs a DMARC check on the From: header domain (example.com. It checks alignment: did SPF pass with example.com as envelope domain (yes, in this case), or did DKIM pass with d=example.com (yes). DMARC pass.
6. Gmail applies internal heuristics (sender reputation, content scoring) and decides where to route the message. DMARC pass plus reasonable reputation = inbox.
Every one of those DNS lookups is a potential failure point. Missing record, slow nameserver, DNSSEC validation failure, expired DKIM selector — any of them produces "mail goes to spam" or "mail bounces" with no clear error message at the user level.
Common misconfigurations and how to spot them
Two SPF records on the same domain: silent permerror, breaks all outbound auth. Fix by merging the includes into a single record.
SPF pointing to a CNAME that is itself another SPF record: works in practice but counts as multiple lookups and is fragile. Resolve and inline the includes.
Wildcard MX: a domain with *.example.com IN MX ... accepts mail for every subdomain, often unintentionally. Common cause of phishing-prep when a domain is acquired but not audited. Replace with explicit MX per subdomain that needs mail.
DMARC with `rua=` pointing to a non-existent mailbox: the policy is published but no one ever sees the reports. Set up a real mailbox (or analytics service) before publishing.
Reverse DNS (PTR) mismatch: when your sending IP's PTR record does not match the HELO hostname, Gmail and Microsoft penalize. Check with dig -x and verify the result matches your mail server's HELO name.
TXT record split across multiple strings: TXT records longer than 255 characters are split. Most resolvers concatenate transparently, but some legacy clients and tools do not. Use 255-char chunks if you must, or move to a CNAME-pointed dedicated subdomain.
Missing AAAA records when sending IPv6: if your mail server connects via IPv6 and you do not have AAAA records for your hostnames, reverse-lookup failures cascade. Either disable IPv6 outbound or publish complete dual-stack records.
CheckFast DNS Checker shows every record at a glance — A, AAAA, MX, TXT, NS, SOA, CNAME — with validation flags.
Run a DNS auditDNS propagation and TTL strategy
DNS "propagation" is a misnomer. DNS does not propagate — caches expire. When you change a record, every resolver that has the old record cached keeps using the old value until its TTL expires. The new value is fetched on next request. There is no broadcast, no push notification, just lazy cache invalidation.
For changes you might want to roll back: lower the TTL to 60-300 seconds 24-48 hours in advance. After the TTL has been low for at least one full TTL cycle, make the change. Caches are now refreshing every minute; rollback is fast. After the change is stable, raise the TTL back to 3600+ seconds for performance.
Common TTL targets: NS records (long, 86400 or higher because changing nameservers is rare and expensive); A and CNAME records (300-3600, balancing convenience vs cache hit rate); MX records (3600 typical, raise after stability); TXT records used for verification (300, since they change often during onboarding).
Negative caching (NXDOMAIN responses) is governed by the SOA record's minimum TTL field. If you publish a record after a missing-record cache, the negative response can persist longer than expected. Set SOA minimum TTL to a low value during active changes.
Frequently asked
There is no DNS-level way to enumerate selectors — you can only query for known names. The two paths: inspect a real outbound message and read the DKIM-Signature header's s= parameter; or check your ESP's documentation which lists their default selectors. CheckFast's Email Checker probes the most common selectors automatically.
Probably not yet. DNSSEC is operationally heavy and protects against an attack class (DNS hijacking) that is rare for SaaS. The protection it adds is real but small relative to the operational risk of mis-rotation. Wait until DNSSEC is one-click on your registrar and platform — Cloudflare and Route 53 are the easiest paths in 2026.
-all (hardfail) tells receivers to reject mail not matching the record. ~all (softfail) tells receivers to mark it suspicious but still consider delivery. During SPF rollout use ~all to avoid breaking unknown legitimate sources; tighten to -all only after DMARC reaches reject and you have full visibility. The difference matters less in 2026 because DMARC is the actual policy layer.
Equal to your TTL plus a few seconds for the actual DNS query. If your TTL is 3600 seconds (one hour) worst case you wait one hour. The myth that DNS takes 24-48 hours dates from the era of higher TTLs and zone-file-based DNS that was synced by FTP. Modern DNS providers update authoritative records in seconds.
Yes. Cloudflare DNS works fine without proxying — set the orange-cloud toggle to gray (DNS only) for any record you want to bypass Cloudflare's CDN. This gives you Cloudflare's fast free DNS without committing the rest of your traffic to them.
3600 seconds (1 hour) is a reasonable default for most records. NS records can be 86400 (24 hours) since they rarely change. Records you might emergency-change (A records pointing to load balancers MX records during ESP migration) should drop to 300 in the days before the change and rise back after.
Related reading
Email deliverability
DMARC From Zero to Reject: A Step-by-Step Rollout
13 min read
Security
SSL Renewal Strategies: Comparing Let's Encrypt, ZeroSSL, and Caddy Auto-Renewal
14 min read
Business & strategy
TLD Strategy for Startups: .com vs .io vs .ai vs .dev
11 min read
Monitoring
Monitoring Cron Jobs Without Cronhub: Healthchecks, Heartbeats, and CheckFast's Approach
11 min read