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

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='

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

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.

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.

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." Feature documentation is at /en/docs, pricing questions are answered in the FAQ, and you can start testing from the free signup.