# How SPF, DKIM and DMARC work together, and what to do when one fails (https://app.resellerhq.com/blog/how-spf-dkim-and-dmarc-work-together)

By ResellerHQ. Published 2026-10-15, updated 2026-10-29.

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](/tools/email-authentication), 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? [#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](/tools/spf-checker) reads a domain's published record and follows every include, the [DKIM checker](/tools/dkim-checker) reads a signature against its selector, and the [DMARC checker](/tools/dmarc-checker) reads the policy and explains what a receiver does with failing mail.

## Why does DMARC need alignment, not just a passing SPF? [#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](https://www.rfc-editor.org/rfc/rfc7489.html#section-3.1):

* **Relaxed** (`r`, the default) - the organisational domain has to match. `mail.example.com` aligns with `example.com`.
* **Strict** (`s`) - the fully qualified domain has to match exactly. `mail.example.com` does not align with `example.com` under 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](/tools/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](/tools/dmarc-record-parser), which reads the same grammar the live checker does.

## What causes SPF to fail, and does flattening fix it? [#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](https://www.rfc-editor.org/rfc/rfc7208.html#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](/tools/spf-flattening-explainer), and check a record you are still drafting with the [SPF record parser](/tools/spf-record-parser) before it is anywhere near a DNS lookup budget.

## Where do I find the DKIM selector for my mail provider? [#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](https://knowledge.workspace.google.com/admin/security/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](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-spf-configure), [Microsoft Learn: DKIM for custom domains](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-dkim-configure) |
| 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](https://www.fastmail.help/hc/en-us/articles/360060591153)                                                                                                                                                                    |
| 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](https://www.zoho.com/mail/help/adminconsole/spf-configuration.html), [Zoho Mail: DKIM configuration](https://www.zoho.com/mail/help/adminconsole/dkim-configuration.html)                                                       |

Four setup checkers run this comparison directly against a domain's live DNS: the [Google Workspace setup checker](/tools/google-workspace-setup-checker), the [Microsoft 365 setup checker](/tools/microsoft-365-setup-checker) (which also flags a missing `selector2` - the single most common Microsoft 365 DKIM mistake), the [Fastmail setup checker](/tools/fastmail-setup-checker), and the [Zoho Mail setup checker](/tools/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](/tools/spf-checker) and [DKIM checker](/tools/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? [#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](/tools/dmarc-record-parser); a domain that has already published one is read live by the [DMARC checker](/tools/dmarc-checker).

The failure mode with no visible error is an external `rua` address. RFC 7489 [section 7.1](https://www.rfc-editor.org/rfc/rfc7489.html#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? [#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](/tools/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](/tools/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](https://learn.microsoft.com/en-us/defender-office-365/message-headers-eop-mdo):

* **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 DMARC `p=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? [#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.

1. **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.
2. **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.
3. **Publish DMARC at `p=none` and 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 your `rua` address receives directly.
4. **Move to `p=quarantine`, then `p=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 at `p=none` exists to prevent.

If you are setting up Google mailboxes for clients, our [mailboxes product](/products/mailboxes) publishes the domain's MX, SPF, DKIM and DMARC records as part of each order, and its DNS check says whether every mail record is in place. For the domain side, see [how many domains and mailboxes cold email infrastructure needs](/blog/cold-email-infrastructure-domains-and-mailboxes).


## Frequently asked questions

### What is the difference between SPF, DKIM and DMARC?
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.

### Why did my DMARC check fail even though SPF passed?
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.

### How many DNS lookups does an SPF record allow?
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.

### What does compauth=fail mean in a Microsoft 365 header?
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.

### Can a DMARC report be sent to an email address on a different domain?
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.

### What order should I fix SPF, DKIM and DMARC problems in?
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](https://www.rfc-editor.org/rfc/rfc7208.html#section-4.6.4)
- [IETF: RFC 7489, DMARC, section 3.1 (identifier alignment)](https://www.rfc-editor.org/rfc/rfc7489.html#section-3.1)
- [IETF: RFC 7489, DMARC, section 7.1 (external destination verification)](https://www.rfc-editor.org/rfc/rfc7489.html#section-7.1)
- [Google Workspace: Set up SPF to help prevent spoofing](https://knowledge.workspace.google.com/admin/security/set-up-spf)
- [Microsoft Learn: Set up SPF to identify valid email sources for your custom cloud domains](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-spf-configure)
- [Microsoft Learn: How to use DKIM for email in your custom domain](https://learn.microsoft.com/en-us/defender-office-365/email-authentication-dkim-configure)
- [Fastmail: Advanced DNS settings for custom domains](https://www.fastmail.help/hc/en-us/articles/360060591153)
- [Zoho Mail: SPF configuration](https://www.zoho.com/mail/help/adminconsole/spf-configuration.html)
- [Zoho Mail: DKIM configuration](https://www.zoho.com/mail/help/adminconsole/dkim-configuration.html)
- [Microsoft Learn: Anti-spam message headers (compauth reason codes)](https://learn.microsoft.com/en-us/defender-office-365/message-headers-eop-mdo)