Guides
How SPF, DKIM and DMARC work together, and what to do when one fails
SPF checks who may send, DKIM checks the message was not altered, and DMARC ties the two together and tells a receiver what to do on failure. Fix SPF first, then DKIM by provider, then move DMARC to enforcement.
The short answer
SPF proves a message came from a server the domain's owner listed. DKIM proves the message was not altered after it was signed. DMARC does not check anything itself: it tells a receiver what to do when SPF or DKIM fails, and that only happens if the passing record is also aligned to the From: domain. A passing SPF record on a domain that does not match From: still fails DMARC. Fix SPF first (it breaks nothing), then DKIM per provider, then move DMARC from p=none to enforcement once both report clean.
SPF proves a message came from a server the domain's owner listed. DKIM proves the message was not altered after it was signed. DMARC does not inspect the message at all - it tells a receiver what to do when SPF or DKIM fails, and only takes effect once the passing record is also aligned to the From: address. That last part is the one most setups get wrong: a domain can publish a perfectly valid SPF record, have it pass, and still fail DMARC.
This guide covers what each of the three proves, why alignment catches setups that look fine, what actually causes each one to fail, and the order to fix them in. It links to the checkers and parsers in our email authentication tools, each of which answers one record or one provider; this page is where the three meet.
What does each of SPF, DKIM and DMARC actually prove?
Each protocol checks a different thing, and none of them alone proves a message is legitimate.
| Protocol | What it checks | What a pass actually proves | What it does not prove |
|---|---|---|---|
| SPF | The sending server's IP address against a DNS TXT record the domain publishes | The message's envelope sender (MAIL FROM) domain authorised this server to send | Nothing about the From: address shown to the reader, and nothing about the message body |
| DKIM | A cryptographic signature in the message header against a public key published at a DNS selector | The signed parts of the message have not changed since the domain named in d= signed it | Nothing about whether that signing domain matches the From: address the reader sees |
| DMARC | Whether the SPF or DKIM domain that passed is aligned with the From: domain | Tells a receiver what to do with mail that fails, and where to send a report about it | Nothing by itself - DMARC has no mechanism of its own, only a policy applied to SPF's and DKIM's results |
Try each one directly: the SPF checker reads a domain's published record and follows every include, the DKIM checker reads a signature against its selector, and the DMARC checker reads the policy and explains what a receiver does with failing mail.
Why does DMARC need alignment, not just a passing SPF?
Because SPF and DKIM each check a different address than the one a reader actually sees, and neither one checks that address at all.
SPF checks the envelope sender (MAIL FROM), not the From: header a mail client displays. A sender can register a domain, publish a valid SPF record for it, and send mail with a completely different From: address - SPF passes and proves nothing about that visible address. DKIM has the same gap in the other direction: the domain in d= only has to match whichever private key signed the message, not the From: domain.
DMARC closes that gap by requiring alignment: the domain that passed SPF or DKIM has to match the From: domain, under one of two modes RFC 7489 defines in section 3.1:
- Relaxed (
r, the default) - the organisational domain has to match.mail.example.comaligns withexample.com. - Strict (
s) - the fully qualified domain has to match exactly.mail.example.comdoes not align withexample.comunder strict mode.
The aspf tag sets the mode for SPF alignment and adkim sets it for DKIM; a record that omits either defaults to relaxed. Our DMARC checker reads whichever mode a domain actually publishes and spells out what it does in practice, rather than just naming the tag - a published aspf=s means "the SPF domain must match this domain exactly," not a generic definition of strict mode.
The result that trips people up: a message can pass SPF cleanly and still fail DMARC, because the domain that passed SPF was never the one in From:. A domain drafting a DMARC record before publishing it can check its tags with the DMARC record parser, which reads the same grammar the live checker does.
What causes SPF to fail, and does flattening fix it?
The most common cause is not a wrong server in the record - it is too many DNS lookups.
RFC 7208 section 4.6.4 caps the number of include, a, mx, ptr, exists mechanisms and the redirect modifier at 10 across the entire chain a receiver has to follow, counting every nested include a provider's own record pulls in. Exceed that and the record returns PermError, which means every server the record lists is treated as unlisted - not just the terms past the limit. A record that authorises ten legitimate senders and trips the ceiling on the eleventh lookup fails for all ten.
This happens easily once a domain sends through more than two or three providers, because each one's include: can itself cost several lookups. SPF flattening collapses an include into the literal IP ranges it currently resolves to, trading the lookup for a value that can go stale the moment the provider changes its infrastructure - it is a way back under the ceiling, not a fix for whatever caused the record to grow. Run a domain's own count against the limit with the SPF flattening explainer, and check a record you are still drafting with the SPF record parser before it is anywhere near a DNS lookup budget.
Where do I find the DKIM selector for my mail provider?
A generic DKIM checker can read whatever selector you give it, but it cannot tell you which selector your provider actually uses - Google Workspace, Microsoft 365 and Fastmail each publish a fixed, documented value, and the published value is the only thing worth checking against. Zoho is the exception: it documents a fixed SPF include, but the DKIM selector is chosen per account when the key is generated, so only the SPF side has a fixed value to check.
| Provider | SPF include to publish | DKIM selector or CNAME pattern | Source |
|---|---|---|---|
| Google Workspace | include:_spf.google.com | TXT at google._domainkey, generated in the admin console | Google Workspace: Set up SPF |
| Microsoft 365 | include:spf.protection.outlook.com | Two CNAMEs, selector1._domainkey and selector2._domainkey, both pointing at a target Microsoft computes from the domain (with periods replaced by dashes) and the tenant's initial .onmicrosoft.com domain - new custom domains added since May 2025 use an updated target format ending -v1.dkim.mail.microsoft | Microsoft Learn: Set up SPF, Microsoft Learn: DKIM for custom domains |
| Fastmail | include:spf.messagingengine.com | Three CNAMEs, fm1._domainkey, fm2._domainkey and fm3._domainkey, each pointing to fm{1,2,3}.<your-domain>.dkim.fmhosted.com | Fastmail: Advanced DNS settings |
| Zoho Mail | include:zohomail.com (or include:one.zoho.com if you also use other Zoho services, to avoid a separate SPF lookup failure) | TXT at a zoho selector, generated in the Zoho Mail admin console | Zoho Mail: SPF configuration, Zoho Mail: DKIM configuration |
Four setup checkers run this comparison directly against a domain's live DNS: the Google Workspace setup checker, the Microsoft 365 setup checker (which also flags a missing selector2 - the single most common Microsoft 365 DKIM mistake), the Fastmail setup checker, and the Zoho Mail setup checker.
None of these checkers can see inside your provider's admin console - they confirm what is published in DNS, not whether DKIM signing is actually switched on there.
We looked for an equivalent fixed value to check for Mailchimp, and did not find one: Mailchimp's own documentation does not publish a single SPF include or DKIM selector that applies the way Google's, Microsoft's, Fastmail's and Zoho's do, so there is no published value for a dedicated checker to compare against. If you send through Mailchimp, the general SPF checker and DKIM checker still read whatever record Mailchimp's own setup instructions told you to publish.
What does a failing or missing DMARC record look like?
Most DMARC problems are visible in the published record itself, once you know what each tag does - and the one failure mode that is not visible is a reporting address that silently gets skipped.
A record is read tag by tag: p (the policy - none, quarantine or reject), pct (what share of failing mail gets that policy), rua and ruf (where aggregate and forensic reports go), and aspf/adkim (alignment mode, covered above). A domain still drafting a record, or one it got from a vendor, can check the text itself with the DMARC record parser; a domain that has already published one is read live by the DMARC checker.
The failure mode with no visible error is an external rua address. RFC 7489 section 7.1 requires that when a DMARC record's report address is on a different domain than the one being reported on, the receiving domain must explicitly authorise it - by publishing a TXT record at <reported-domain>._report._dmarc.<receiving-domain> containing v=DMARC1. Without that authorisation record, a receiver sending aggregate reports is required to silently skip the address. Nothing about the DMARC record itself looks wrong; the reports simply never arrive, and a domain that thinks it is being monitored is not.
How do I read what already happened in the headers?
A message you already received or sent carries its own verdicts in the Authentication-Results header, stamped by whichever service handled it - reading that header tells you what happened to one specific message, as distinct from what a checker predicts would happen to future mail.
Paste a raw header block into the email header analyser to see every Received hop and the authentication verdicts in order, newest first, since each hop a message passes through adds its own line above the ones before it. For the Authentication-Results line on its own, the Authentication-Results header explainer reads each method's verdict and the domain it was about.
Mail delivered through Microsoft 365 carries one more thing: a compauth= result that is Microsoft's own addition, not part of any standard. Microsoft's documentation is explicit that "despite a compauth failure, the message might still be allowed if other assessments don't indicate a suspicious nature" - so compauth=fail on its own is not proof a message was blocked. The three-digit reason code next to it is the useful part. Two worth knowing by number, from Microsoft's own documentation:
- 001 - the message failed implicit authentication because the sending domain had no published authentication records at all, or had only a weaker policy (an SPF
~all/?all, or a DMARCp=none). - 601 - the message failed because the sending domain is one of your own organisation's accepted domains, which usually means something inside your own tenant is spoofing itself.
A code that is not in Microsoft's published table is one they have not documented; guessing at its meaning is how a header gets read backwards.
What order should I fix SPF, DKIM and DMARC in?
SPF first, then DKIM by provider, then move DMARC enforcement up only once both report clean.
- Fix SPF first. It is the cheapest change and breaks nothing else while you make it: publish the record, confirm it resolves under the 10-lookup ceiling, and flatten only the includes that are actually pushing you over it.
- Fix DKIM next, one provider at a time. Use the fixed selector or CNAME pattern each provider documents - the table above - rather than guessing, and confirm signing is actually switched on in that provider's own console, since no checker here can see that.
- Publish DMARC at
p=noneand watch, before enforcing anything. This collects aggregate reports without rejecting or quarantining any mail. We do not yet have a tool that reads those aggregate reports - a DMARC aggregate report reader is planned but not built, so for now reading them means opening whatever yourruaaddress receives directly. - Move to
p=quarantine, thenp=reject, only once the reports show nothing legitimate is failing. Enforcing before that point risks blocking your own mail, which is exactly what the monitoring step atp=noneexists to prevent.
If you are provisioning Google Workspace or Microsoft 365 mailboxes for clients, our mailbox licences product covers the licence side of this; DNS records, including the ones this guide walks through, still have to be published separately, since licence provisioning does not configure DNS. For the domain side, see how many domains and mailboxes cold email infrastructure needs.
Questions
- SPF publishes which mail servers may send for a domain and is checked against the message's envelope sender. DKIM adds a cryptographic signature to a message so a receiver can tell it was not altered after signing. DMARC does not check the message itself; it tells a receiver what to do when SPF or DKIM fails and where to send a report, but only once it has confirmed the passing check is aligned to the From: address.
- DMARC needs the domain that passed SPF (or DKIM) to also match the From: address domain, a requirement called alignment. RFC 7489 defines two modes: strict, where the domains must match exactly, and relaxed, where a subdomain of the same organisational domain also counts. A message that passes SPF for a completely different domain than the one in From: still fails DMARC, because nothing aligned.
- RFC 7208 section 4.6.4 caps the include, a, mx, ptr, exists and redirect terms at 10 across the whole chain, and a record that needs more returns a PermError - the entire record is then treated as if it authorised nobody, not just the mechanisms past the limit.
- compauth is Microsoft's own composite authentication result, not a standard. A fail does not by itself mean the message was blocked - Microsoft's documentation states a composite authentication failure can still be delivered if nothing else about the message looks suspicious. The three-digit reason code is the useful part: 001 means the sending domain had no authentication records or only a weak policy, and 601 means the domain is spoofing one of your own accepted domains.
- Only if that domain explicitly agrees to it. RFC 7489 section 7.1 requires the receiving domain to publish a TXT record at <reported-domain>._report._dmarc.<receiving-domain> containing v=DMARC1 before a receiver will send reports there. Without it, a receiver sending aggregate reports must skip that address, and nothing in the DMARC record itself tells you this happened.
- SPF first, because a wrong SPF record breaks nothing else and is cheap to correct. Then DKIM, per mail provider, since each publishes its own fixed selector or CNAME pattern rather than a value a generic checker can guess. Only once both report clean move a DMARC record from p=none to quarantine, then reject - enforcing before you have checked what is actually failing risks blocking your own legitimate mail.
Sources
- IETF: RFC 7208, Sender Policy Framework (SPF), section 4.6.4 rfc-editor.org
- IETF: RFC 7489, DMARC, section 3.1 (identifier alignment) rfc-editor.org
- IETF: RFC 7489, DMARC, section 7.1 (external destination verification) rfc-editor.org
- Google Workspace: Set up SPF to help prevent spoofing knowledge.workspace.google.com
- Microsoft Learn: Set up SPF to identify valid email sources for your custom cloud domains learn.microsoft.com
- Microsoft Learn: How to use DKIM for email in your custom domain learn.microsoft.com
- Fastmail: Advanced DNS settings for custom domains fastmail.help
- Zoho Mail: SPF configuration zoho.com
- Zoho Mail: DKIM configuration zoho.com
- Microsoft Learn: Anti-spam message headers (compauth reason codes) learn.microsoft.com