2026-06-04

SPF, DMARC, and DKIM Setup Guide: Verify and Fix Mail Auth

mail authentication SPF DMARC DKIM

Stop Discovering Broken Mail Auth Through Client Complaints

SPF and DMARC feel like a one-time setup, but DNS changes break them silently and repeatedly. Agencies managing multiple client domains are especially exposed: a new email SaaS added to an SPF record, a nameserver migration that drops authentication records, or a DKIM key rotation that misses the publish step. The first sign of failure is almost always a client saying "emails aren't arriving."

This guide covers the correct configuration and verification procedure for SPF, DMARC, DKIM, and MX records — with commands you can run immediately during initial setup, domain handoffs, or routine audits.

Understanding the Four Authentication Types

Type Purpose Where it lives
MX Declares the servers that accept inbound email TXT record at the apex domain
SPF Declares which servers are authorized to send TXT record at the apex domain
DMARC Specifies what happens when SPF or DKIM fails TXT record at _dmarc.<domain>
DKIM Cryptographically signs outbound messages TXT record at <selector>._domainkey.<domain>

Each type is independent. A valid SPF record does not protect you if the DMARC policy is malformed — email may still land in spam.

SPF: Correct Configuration

Basic syntax

example.com.  IN  TXT  "v=spf1 include:_spf.google.com ~all"
  • v=spf1 — Version tag. Must appear first.
  • include: — Authorizes an external set of sending servers.
  • ~all — Soft fail for unauthorized senders (recommended starting point).
  • -all — Hard fail: reject unauthorized senders entirely (stricter enforcement).

Common SPF mistakes

Mistake Consequence
Missing v=spf1 prefix Record is not recognized as SPF — effectively disabled
Two v=spf1 records on the same domain RFC 7208 violation — receiving MTAs treat this as permanent failure
SPF lookup chain exceeds 10 DNS queries RFC 7208 limit exceeded — evaluation fails permanently
No all terminal Policy is incomplete — sender evaluation is undefined

Verify SPF with dig

# Extract TXT records starting with v=spf1
dig +short TXT example.com | grep 'v=spf1'

# Check that exactly one SPF record is returned
# Two or more records require immediate remediation

If dig isn't available, nslookup works too (it can't filter, so scan the output for the right line).

nslookup -type=TXT example.com

DMARC: Correct Configuration

Basic syntax

_dmarc.example.com.  IN  TXT  "v=DMARC1; p=quarantine; rua=mailto:[email protected]; pct=100"
  • v=DMARC1 — Version tag.
  • p= — Policy: none (monitor only) → quarantine (spam folder) → reject (block). Escalate gradually.
  • rua= — Aggregate report destination as a mailto: URI.
  • pct= — Percentage of messages the policy applies to (0–100).

Common DMARC mistakes

Mistake Consequence
Missing p= tag Required tag absent — the entire record is invalid
pct=120 or other out-of-range value RFC 7489 violation — some parsers silently ignore the record
rua=example.com without mailto: Malformed URI — reports never arrive
Record placed at wrong subdomain Not queried by receiving MTAs

Verify DMARC with dig

# Retrieve the DMARC record
dig +short TXT _dmarc.example.com

# Confirm p= tag is present
dig +short TXT _dmarc.example.com | grep 'p='

With nslookup:

nslookup -type=TXT _dmarc.example.com

If p=none, the policy is monitoring-only and no messages are filtered. Once your authentication is stable, step up to p=quarantine, then eventually to p=reject.

DKIM: Correct Configuration

Public key record syntax

selector1._domainkey.example.com.  IN  TXT  "v=DKIM1; k=rsa; p=MIGfMA0GCSq..."
  • v=DKIM1 — Version tag.
  • k=rsa — Key algorithm (can usually be omitted; defaults to RSA).
  • p= — Base64-encoded public key. An empty p= means the key has been revoked.

Finding your selector name

The selector name depends on which email sending service you use:

Service Common selector
Google Workspace google
SendGrid s1, s2
Amazon SES amazonses
Mailchimp k1
# Verify the DKIM public key for selector "google"
dig +short TXT google._domainkey.example.com

# A valid response contains v=DKIM1 and a non-empty p= value

With nslookup:

nslookup -type=TXT google._domainkey.example.com

Common DKIM mistakes

Mistake Consequence
Publishing new selector key but forgetting DNS All signatures fail for that selector
Not removing old selector after rotation completes Stale records cause confusion; some parsers may match the wrong key
p= value is empty Key is treated as revoked — all signature verifications fail

Verifying MX Records

# Check MX records and priority values
dig +short MX example.com

# Example output:
# 10 aspmx.l.google.com.
# 20 alt1.aspmx.l.google.com.

With nslookup:

nslookup -type=MX example.com

If no MX record is returned, or the record still points to a temporary migration host, inbound email will fail. After any domain or DNS migration, confirm the MX records point to the intended mail servers.

Fixing Changes That Don't Seem to Take Effect (DNS Propagation and Caching)

Right after editing a DNS record, TTL (Time To Live) and resolver caching can delay when the new value becomes visible. If dig still shows the old record, work through these checks before assuming the configuration itself is wrong.

  1. Query a public resolver (Google Public DNS) -- this rules out a stale cache on your own network or ISP resolver.
dig +short TXT example.com @8.8.8.8
nslookup -type=TXT example.com 8.8.8.8
  1. Query the authoritative nameserver directly -- bypassing every cache shows you the true, current state of the record.
# Find the authoritative nameservers
dig +short NS example.com

# Query one of them directly for the TXT record
dig +short TXT example.com @ns1.example-dns.com
  1. Check the TTL -- a longer TTL means a longer wait before the change propagates everywhere. If you need the cutover to happen fast, lower the TTL (e.g. to 300 seconds) before making the actual change.
dig example.com TXT
# Look at the TTL value (in seconds) in the ANSWER SECTION

In most cases a record that "isn't showing up yet" simply hasn't propagated. Only suspect the configuration itself if the record still doesn't match after waiting out the TTL.

Combined Spot-Check Script

Use this during new client onboarding or as part of a periodic audit:

#!/bin/bash
# Usage: ./check-mail-auth.sh example.com [selector]
DOMAIN="${1:?Provide a domain name}"
SELECTOR="${2:-google}"

echo "=== MX ==="
dig +short MX "$DOMAIN"

echo "=== SPF ==="
dig +short TXT "$DOMAIN" | grep 'v=spf1'

echo "=== DMARC ==="
dig +short TXT "_dmarc.$DOMAIN"

echo "=== DKIM (selector: $SELECTOR) ==="
dig +short TXT "${SELECTOR}._domainkey.$DOMAIN"

Once you have confirmed the output looks correct, register each type in Miterl so future changes are caught automatically.

Move From Manual Checks to Continuous Monitoring

Even a correct setup breaks again at predictable moments:

  • Adding a new email SaaS causes the SPF lookup count to exceed 10.
  • A DNS zone migration drops authentication records that were not in the export.
  • An automatic DKIM key rotation at the ESP does not update the published TXT record.

Manual verification catches the state at one point in time. Continuous monitoring catches changes the moment they happen.

Miterl monitors MX, SPF, DMARC, and DKIM records using the same API you use for HTTP and SSL monitors:

# Register a DMARC monitor (validates p= tag, pct range, and rua URI format)
curl -X POST https://miterl.com/api/v1/monitors \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "example.com DMARC",
    "type": "dmarc",
    "url": "example.com",
    "interval_seconds": 300
  }'

Syntax validation is built in. An SPF record that exceeds 10 lookups, a DMARC record missing p=, or a DKIM key with an empty p= field all trigger a Down alert — not just a missing record. For the full setup walkthrough, see "Monitor SPF, DMARC, DKIM, and MX Records Continuously."

Summary

  • SPF: One v=spf1 TXT record at the apex domain, with a confirmed all terminal and a lookup chain under 10 hops.
  • DMARC: Required p= tag at _dmarc.<domain>. Start with none, escalate to quarantine then reject as confidence grows.
  • DKIM: Before rotating a selector, verify the new public key is live in DNS. Confirm the selector at the ESP matches what your DNS serves.
  • MX: After any migration, verify MX records point to the intended mail servers — not a temporary hostname from the migration.

After initial setup, shift from manual checks to continuous monitoring so you catch silent breakage before clients do. For domain-level checks at launch, the "Pre-Launch Monitoring Checklist" covers the full set. To understand how mail authentication fits alongside HTTP, DNS, and SSL monitoring, see "Server Monitoring Basics: HTTP, Ping, DNS, and SSL." Beyond mail authentication, tracking your site's response time gives clients another concrete measure of maintenance quality — "Response Time Monitoring Guide" covers setup and thresholds. When a downtime incident does occur, having a notification template ready saves time: "Downtime Notification Email Templates for Agencies" provides copy for common outage scenarios. For the site itself, see "What Is Synthetic Monitoring? A Plain-Language Guide" for outside-in checks and "Heartbeat Monitoring: Detect Failed Cron and Batch Jobs" for cron and batch jobs. Feature documentation is at /en/docs, pricing questions are answered in the FAQ, and you can start testing from the free signup.