This header block is where most people's understanding of email authentication falls apart:
Authentication-Results: mx.google.com;
spf=pass smtp.mailfrom=bounce@esp-vendor.net;
dkim=pass header.d=esp-vendor.net;
dmarc=fail (p=REJECT) header.from=yourcompany.com
Two passes and a fail. No malformed record anywhere. Nothing to fix in DNS. And the mail is being rejected.
Every part of that is correct behaviour.
The question DMARC actually asks
DMARC does not ask "did SPF pass?"
It asks: did SPF or DKIM pass for the same domain that appears in the From: header?
There are three domains in play in any message, and only one of them is visible to a human:
| Identifier | Lives in | Who sees it | Checked by |
|---|---|---|---|
From: |
The message headers | Your recipient | Nothing, on its own |
Envelope sender (MAIL FROM) |
The SMTP conversation | Nobody | SPF |
DKIM d=
|
The signature header | Nobody | DKIM |
SPF authenticates the envelope sender. DKIM authenticates the signing domain. Neither of them is the From: header. DMARC exists entirely to insist that at least one of them matches it.
In the header above, SPF authenticated esp-vendor.net and DKIM signed as esp-vendor.net. The recipient sees yourcompany.com. Nothing authenticated yourcompany.com, so DMARC fails.
It has to work this way. If DMARC accepted "SPF passed" without the matching step, anyone with a mailbox at a provider with a valid SPF record could send as you and pass.
How you get here without doing anything wrong
You sign up for a marketing platform, an invoicing tool, or a helpdesk. You set the From address to hello@yourcompany.com because that is what your customers should see.
The platform sends from its own infrastructure with its own envelope domain — it has to, because that is where bounces go. SPF passes, for them. Your From: says you.
Those do not align. If nothing is DKIM-signed with your domain, every message from that platform fails DMARC.
This is the single most common cause of "we set up SPF and it still doesn't work".
The fix that feels right and does nothing
Adding include:esp-vendor.net to your SPF record.
It is the intuitive move. It changes nothing.
That include authorises their servers to send for your envelope domain. Their mail still uses their envelope domain, so the alignment comparison is untouched. You have spent one of your ten SPF DNS lookups for no benefit at all.
If your reaction to a dmarc=fail is to edit your SPF record, stop and read the Authentication-Results header first.
Relaxed vs strict, and the trap in the middle
Alignment has two modes, set per-mechanism with aspf= and adkim= in your DMARC record:
-
Relaxed (
r, the default) — organisational domains must match.mail.example.comaligns withexample.com. -
Strict (
s) — exact match only.mail.example.comdoes not align withexample.com.
Relaxed is right for almost everyone. Strict is worth it only if you are confident every sender uses the exact apex domain, and it breaks the day someone starts sending from a subdomain.
Here is the part that matters if you are implementing this yourself: "organisational domain" is not "the last two labels".
attacker.co.uk and victim.co.uk share their last two labels and have no relationship whatsoever. Getting this right means consulting the Public Suffix List, not counting dots. A checker that counts dots will tell you two unrelated domains are aligned, which is the worst possible direction to be wrong in.
Reading it from your own mail
Send a message to a mailbox at a different provider. Open the delivered copy and view the source — ⋮ → Show original in Gmail, View message details in Outlook.
Then compare exactly three lines:
From: Your Company <hello@yourcompany.com> <- the domain that must be matched
spf=pass smtp.mailfrom=bounce@esp.net <- authenticated esp.net
dkim=pass header.d=esp.net <- signed by esp.net
If neither the smtp.mailfrom domain nor the DKIM d= matches your From domain, DMARC fails regardless of how correct your SPF record is.
Use a message you received, not one from your Sent folder. Sent copies have no Authentication-Results header at all — that verdict is written by the receiving server. Nearly everyone trips on this the first time and concludes their headers are broken.
Fixing it properly
Get DKIM signing with your own domain. Every serious sending platform supports it. It is usually called "authenticate your domain", "domain authentication" or "custom DKIM", and it hands you a selector to publish in DNS. Once the platform signs with d=yourcompany.com, DKIM aligns and DMARC passes no matter what the envelope sender says.
There is a second reason to prefer DKIM, and it is the one that bites later: DKIM survives forwarding and SPF does not. The moment a message is forwarded, the forwarding server is not in your SPF record. Every mailing list and every user-configured forward becomes a DMARC failure if SPF alignment is all you have.
Optionally, also set a custom return-path. Some platforms let you point the envelope sender at a subdomain of yours — "custom return-path" or "custom bounce domain". You publish a CNAME, the envelope domain becomes bounce.yourcompany.com, and that aligns under relaxed. This fixes only the SPF half and still dies on forwarding, so do it as well as DKIM, not instead.
Before you enforce
This is the actual reason the p=none → quarantine → reject order exists.
Run at p=none with a reporting address and read the aggregate reports. They tell you which sources pass SPF and DKIM with alignment — a different and much smaller set than the sources that merely pass SPF. Move to p=quarantine only when that list matches the systems you expect to be there.
Skip it and you will find your misaligned senders the hard way: by having their mail rejected.
I maintain notspoofed — a free, no-signup checker for SPF, DKIM and DMARC. It has an in-browser header analyzer that does the three-way alignment comparison above and shows relaxed vs strict as a table; headers are parsed locally and never uploaded. It uses the Public Suffix List for organisational domains, because counting labels is wrong. Source is on GitHub under MIT.
If you have hit an alignment failure with a cause that is not on this list, I would like to hear it — those are the interesting ones.
Top comments (0)