SPF, DKIM and DMARC: what each one actually does
Three records, three different jobs. What each one proves, why DMARC needs the other two, and a rollout order that will not break delivery.
Three records, three different jobs
The three are constantly lumped together, which hides the fact that they answer completely different questions. Getting the distinction straight is most of the work:
| Record | Question it answers | Survives forwarding? |
|---|---|---|
| SPF | Was this message sent from a server the domain authorised? | No |
| DKIM | Was this message signed by the domain, and unaltered since? | Usually |
| DMARC | What should the receiver do when the other two fail — and where should it report? | n/a |
SPF checks the envelope sender against an IP list. DKIM checks a cryptographic signature. DMARC checks that whichever of those passed refers to the same domain the recipient sees in the From header, and then tells the receiver what to do about it.
SPF: which servers may send
SPF is a single TXT record at your domain apex listing the sources allowed to send mail for it. A minimal one looks like:
v=spf1 include:_spf.google.com ~all
Reading it left to right: use SPF version 1; treat anything in Google's published list as authorised; for everything else, soft-fail.
The last mechanism is the one worth deciding deliberately:
-all(fail) — anything not listed should be rejected. The strongest setting, and the one to reach eventually.~all(softfail) — accept but mark as suspicious. The right setting while you are still discovering senders.?all(neutral) — says nothing, which is barely better than having no record.+all— authorises the entire internet. Never correct.
SPF's fundamental weakness is forwarding. When a mailing list or a forwarding address relays your message, the receiving server sees the forwarder's IP, which is not in your record, and SPF fails through no fault of yours. This is exactly the gap DKIM covers.
DKIM: proof the message was not altered
DKIM adds a cryptographic signature to each outgoing message, covering the body and a chosen set of headers. The receiver fetches your public key from DNS and verifies it.
The key lives at a selector-scoped name — selector1._domainkey.example.com — because a domain can have several keys in use at once. That is deliberate: it is what makes key rotation possible, and it is why you cannot check DKIM without knowing the selector your provider uses.
Because the signature travels with the message, DKIM normally survives forwarding. It breaks when something modifies the signed parts in transit — a mailing list appending a footer or rewriting the subject is the classic case.
Alignment: the part that ties them together
This is the concept that makes DMARC more than a wrapper, and the one most explanations skip.
A message has two "from" addresses. The envelope sender (Return-Path) is what SPF checks. The From header is what the recipient actually sees. They do not have to match — and for a long time, that was the whole problem: an attacker could pass SPF for a domain they control while putting your domain in the visible From.
DMARC closes it by requiring alignment: the domain that passed SPF or DKIM must match the From-header domain.
- Relaxed alignment (the default) — the organisational domain must match, so
mail.example.comaligns withexample.com. - Strict alignment — the domains must match exactly.
This is why a message can pass SPF and still fail DMARC, which is one of the more confusing results to debug. The SPF check succeeded — for a different domain than the one in the From header.
DMARC: what to do when a check fails
DMARC is a TXT record on the _dmarc subdomain:
v=DMARC1; p=none; rua=mailto:[email protected]; pct=100; adkim=r; aspf=r
The tags that matter:
| Tag | Meaning |
|---|---|
| p | Policy for failures: none (monitor), quarantine (spam folder), reject (refuse). |
| rua | Where to send aggregate reports. Without this, DMARC tells you nothing. |
| pct | Percentage of failing mail the policy applies to — the lever for a gradual rollout. |
| sp | Policy for subdomains. Defaults to p; set it explicitly if subdomains differ. |
| adkim / aspf | Alignment mode, r (relaxed, default) or s (strict). |
A record with p=none and no rua is the worst of both worlds: it looks like you have DMARC, enforces nothing, and reports nothing. If you publish only one thing today, publish p=none with a reporting address.
A rollout order that will not lose mail
The order matters, because enforcing before you have an inventory of your senders is how legitimate mail starts disappearing.
- Inventory what sends as you. Marketing platform, helpdesk, invoicing, CI notifications, the CRM someone set up years ago. This list is always longer than expected.
- Publish SPF with
~all, covering every source you found. Soft-fail, not hard-fail, at this stage. - Enable DKIM signing on every platform that supports it, and publish each key.
- Publish DMARC with
p=noneand aruaaddress. Nothing is enforced; reports start arriving. - Read the reports for two to four weeks. They will surface senders that fail — some of which are legitimate systems you missed. Fix those before enforcing anything.
- Move to
p=quarantine, optionally withpct=10first and raising it as it stays quiet. - Move to
p=rejectonce quarantine has been clean for a few weeks. - Tighten SPF to
-alllast, when you are confident the source list is complete.
Every step is reversible until p=reject. Do not skip the reporting stage to get there faster — that stage is the entire reason the process is safe.
The mistakes that actually break delivery
- Two SPF records. Adding a provider's suggested record alongside an existing one, instead of merging its
includeinto it. This is a PermError, and it fails everything. - Blowing the 10-lookup limit. Four or five providers, each nesting their own includes, gets there quickly. Flatten what you can, and drop senders you no longer use.
- Publishing DMARC at the wrong name. It belongs at
_dmarc.example.com, not at the apex. - Jumping straight to
p=reject. Enforcement without an inventory silently rejects your own invoices. - Forgetting subdomains. Without
sp, subdomains inheritp— which is usually right, but check it rather than assume it. - Using
ptrin SPF. Deprecated, slow, and unreliable. Remove it. - No
ruaaddress. You are flying blind, and you will not find out about a broken sender until someone complains.
Frequently asked questions
Do I need all three, or is SPF enough?
What is the SPF 10-lookup limit?
include, a, mx, ptr, exists and redirect counts, and includes nest — one provider’s include can consume several on its own. Exceeding it is a PermError, and many receivers treat that as an SPF failure rather than ignoring it.Can I publish two SPF records?
v=spf1. Two is a PermError, and it is the single most common SPF misconfiguration — usually caused by adding a second provider’s record instead of merging its include into the existing one.Where does the DKIM record live?
<selector>._domainkey.<domain>. The selector is chosen by whoever signs your mail, so you have to get it from your provider — there is no way to discover it from DNS alone, which is why a DKIM check needs you to supply it.Is p=none pointless?
p=none nothing is rejected, but you start receiving aggregate reports showing every source sending as your domain, including ones you had forgotten. That inventory is what makes it safe to move to quarantine later.Check it on your own domain
Everything above is only useful if you can see the current state of your own setup. These tools do exactly that:
Spotted an error in this guide? Tell us at [email protected] — corrections are made and the updated date above changes with them.