All posts Deliverability

SPF, DKIM, and DMARC explained for developers

What SPF, DKIM, and DMARC each do, how they fit together to stop email spoofing, and how to read the DNS records and authentication headers to tell whether they're working.

Erik Vlčák
Erik Vlčák
Customer Success Engineer
7 min read

Open a terminal, and you can send an email claiming to be from support@stripe.com. SMTP won't stop you. The protocol was designed in the early 1980s with no built-in way to verify the sender, and that gap is still there. Three standards have been retrofitted on top to close it: SPF, DKIM, and DMARC. Together they let a receiving server decide whether mail claiming to be from a domain actually came from someone the domain authorized.

SPF: declaring who can send for you

SPF is the first record receivers check, and the easiest to get right, so it's the place to start. It's a single DNS TXT record listing the IP addresses allowed to send email on behalf of your domain. When a receiving server gets a message claiming to be from yourapp.com, it looks up that record and checks whether the sending IP is on the list.

v=spf1 include:spf.lettr.com include:_spf.google.com ~all

The include mechanisms pull in the IP ranges of each service that sends for you. The trailing ~all is the catch-all verdict for everything else: ~all soft-fails (accept but mark suspicious), -all hard-fails. Stick with ~all.

Check the record with dig, and confirm there's exactly one:

dig TXT yourapp.com +short

Unfortunately, SPF has one structural weakness worth remembering: it checks the envelope sender (the MAIL FROM in the SMTP conversation), not the From header users see. A phishing email can fail SPF on its real domain and still display any From address it likes. SPF also breaks under forwarding, because the forwarder's IP isn't in your record. Both gaps are why DKIM and DMARC exist.

DKIM: signing each message cryptographically

Where SPF authorizes servers, DKIM authenticates the message itself, which is what lets it survive the forwarding that breaks SPF. The provider computes a hash of selected headers and the body, signs it with a private key, and attaches the result as a DKIM-Signature header:

DKIM-Signature: v=1; a=rsa-sha256; d=yourapp.com; s=lettr;
h=from:to:subject:date:message-id;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk2...

Two fields carry the meaning. d=yourapp.com is the domain claiming responsibility, and s=lettr is the selector that tells receivers where to find the public key: <selector>._domainkey.<domain>. The receiver fetches that key, recomputes the hash, and compares. Change a single character in the subject or body in transit and the signature no longer matches. Fetch the public key yourself:

dig TXT lettr._domainkey.yourapp.com +short

The selector exists to enable key rotation: providers publish a new key under a new selector, start signing with it, and retire the old one once mail in flight has cleared. Because DKIM signs the visible From header and travels with the message, it protects the address users actually see far better than SPF does.

DMARC: policy and reporting

SPF and DKIM each produce a pass or fail, but neither says what a receiver should do with a failure, and neither guarantees the check covers the domain in the visible From header. DMARC fills both gaps. It's a DNS TXT record at _dmarc.yourapp.com that publishes a policy and a reporting address:

v=DMARC1; p=none; rua=mailto:dmarc-reports@yourapp.com

p=none is the policy (what to do with failing mail), and rua is where aggregate reports go. Check it with dig TXT _dmarc.yourapp.com.

Alignment is the part that matters

Alignment is the mechanism that actually closes the spoofing gap, so it's the one DMARC concept worth internalizing. A message passes DMARC only if it passes SPF or DKIM and the domain that passed matches the From header domain the user sees. An attacker who passes SPF on their own domain while displaying yours in the From header now fails, because the authenticated domain and the visible one don't line up.

Roll out enforcement in stages

Jumping straight to p=reject drops entire categories of legitimate mail overnight: forwarded messages, mail from services you forgot about, misconfigured subdomains. The policy moves through three values, and the first one must be the a starting point.

Aggregate reports arrive as gzipped XML at the rua address. One record looks like this:

<record>
<row>
<source_ip>198.51.100.42</source_ip>
<count>1523</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>yourapp.com</header_from>
</identifiers>
</record>

That says 1,523 messages from 198.51.100.42 claimed to be from yourapp.com; DKIM passed, SPF failed. Looking up the IP tells you whether it's a legitimate forwarder or someone spoofing you. Once every legitimate sender passes, move to p=quarantine (failing mail routed to spam), then to p=reject (failing mail refused outright). The full progression usually takes four to eight weeks.

Verifying everything is working

Two checks confirm the records are live and doing their job. First, read them straight out of DNS with dig:

# Check SPF
dig TXT yourapp.com +short | grep spf

# Check DKIM (replace 'lettr' with your selector)
dig TXT lettr._domainkey.yourapp.com +short

# Check DMARC
dig TXT _dmarc.yourapp.com +short

Then send yourself a test email and open "Show original" in Gmail. The recipient adds an Authentication-Results header recording all three verdicts:

Authentication-Results: mx.google.com;
dkim=pass header.d=yourapp.com header.s=lettr;
spf=pass smtp.mailfrom=bounce+abc123@mail.yourapp.com;
dmarc=pass (p=REJECT dis=NONE) header.from=yourapp.com

The goal is dkim=pass, spf=pass, and dmarc=pass. A dmarc=fail with SPF and DKIM both passing almost always means an alignment mismatch, so check the domains first. If you're using Lettr, the domain verification flow runs these checks and flags missing records before you start sending.

FAQ

Do I need all three of SPF, DKIM, and DMARC?
Yes. The three do different jobs and only close the spoofing gap together. SPF names the servers allowed to send, DKIM signs each message so tampering is detectable, and DMARC ties both to the visible From address and says what to do on failure. Gmail and Yahoo require all three for bulk senders.
What does DMARC alignment mean?
Alignment means the domain that passed SPF or DKIM matches the domain in the visible From header. Without it, a message could pass SPF for one domain while displaying another in the From line, which is exactly the spoofing DMARC exists to stop. DMARC passes only when at least one of the two authenticates and aligns with the From domain.
Why should I start DMARC at p=none?
Because p=none reports failures without blocking any mail. The aggregate reports surface every service sending as your domain, including ones you forgot about, so you can fix legitimate senders before enforcement could send their mail to spam. Review the reports for a few weeks, then move to p=quarantine and p=reject.
How do I check whether SPF, DKIM, and DMARC are working?
Read the records from DNS with dig, then send a test email and open "Show original" in Gmail. The receiver records the verdicts in an Authentication-Results header, and the goal is dkim=pass, spf=pass, and dmarc=pass. A dmarc=fail while SPF and DKIM pass almost always points to an alignment mismatch.

Bottom line

Gmail and Yahoo both require all three for bulk senders, and inbox providers increasingly penalize domains that lack them. Without these records, anyone can send mail that appears to come from your domain, and recipients won't inspect the headers. They will assume it was you.

The actual work is a handful of DNS records and a few weeks of monitoring. Set up SPF, let your provider handle DKIM, deploy DMARC at p=none, read the reports, and ratchet up enforcement as confidence grows.

Or skip the manual setup entirely. Create a free Lettr account, add your sending domain, and the SPF and DKIM records are generated and verified for you, with DMARC reporting built in.