Why your password reset emails land in spam
A message can pass SPF and pass DKIM and still fail DMARC, because alignment asks a different question than either of them. Here is how to tell which check is failing and why.
A password reset is the most wanted message your application sends. Someone is sitting at a login screen waiting for it. It is also, reliably, one of the most filtered — and the reason is almost never the content.
Before a receiving mail server reads a single word of your message, it has already run several checks against the connection and the headers. If those go badly, the content never gets a fair hearing. Rewriting the subject line will not help you, because the subject line was never the problem.
The checks that run before your content matters
There are four, and they run in roughly this order. Each one is a question about identity rather than quality:
- Is this server allowed to send for this domain? — SPF
- Has the message been altered since it was signed? — DKIM
- Do those two agree with the address the recipient actually sees? — DMARC alignment
- Does the sending machine look like real infrastructure? — reverse DNS
The third one is where most password resets come undone, and it is the least understood of the four.
Alignment is not the same as passing
A message can pass SPF and pass DKIM and still fail DMARC. That surprises people, so it is worth being precise.
SPF authenticates the envelope sender — the Return-Path, which the recipient never sees. DKIM authenticates whichever domain signed the message, which may not be yours either. DMARC asks a third question: does either of those authenticated identities match the domain in the From header — the one displayed in the mail client?
Here is a message that passes both checks and still fails:
Return-Path: bounces@mail.provider.net ← SPF authenticates this
From: noreply@yourapp.com ← the user sees this
SPF pass (provider.net is authorised)
DKIM pass (signed by provider.net)
DMARC fail (neither aligns with yourapp.com)
Both green ticks, and the message still fails the check that matters. This is the default state of a great many providers, because aligning the return path requires per-customer DNS rather than one shared bounce domain — and shared is cheaper to operate.
If you only check one thing after reading this, check alignment rather than SPF and DKIM individually.
How to tell which one is failing
Send yourself a reset and read the raw headers. The Authentication-Results header records each verdict independently:
Authentication-Results: mx.google.com;
spf=pass smtp.mailfrom=bounces@mail.provider.net;
dkim=pass header.i=@provider.net;
dmarc=fail (p=NONE) header.from=yourapp.com
Three verdicts, one line, and the last one is the one the filter weighs.
Two details worth noticing in that example. The header.from value is the domain DMARC is evaluating — that is your domain, and it is what must align. And p=NONE is your own published policy: it tells receivers you have asked for no enforcement, which means you are getting the reputational damage of a failure without the protection a policy would give you.
What to do about it
In order of effort:
- Publish a DMARC record, even at
p=none, and turn on aggregate reports. You cannot fix what you cannot see, and the reports will tell you within a day which of your sending sources are failing alignment. - Align the return path. This is the fix that resolves the case above, and it is a property of your provider rather than something you can configure around. Ask yours whether the
Return-Pathon your mail is on your domain or theirs. - Move to enforcement —
p=quarantine, thenp=reject— once the reports show your legitimate mail aligning cleanly. Until then, anyone can spoof you and receivers have been told not to care.
Where Pharos fits
Pharos gives every sender domain its own aligned return path and refuses to send from a domain whose records do not verify, so the failure above cannot occur silently. That is a design choice rather than a feature, and you can check it yourself in the headers of the first message you send.