Why your password reset email goes to spam
A password reset is the message with the least tolerance for delay and the highest chance of being filtered. That is not a coincidence. Structurally, a reset email is indistinguishable from a phishing attempt: urgent subject, a link to click, an account at stake, and a sender the recipient rarely hears from otherwise.
5 min read
Diagnose before you change anything
Do not start rewriting copy. Find out which layer is failing, because the fixes are unrelated.
- Did it send? Look up the message id in your delivery log. If your code never called the API, everything below is irrelevant.
- Did it deliver, bounce, or defer? A soft bounce with a rate limit message is a different problem from a hard bounce on an address that does not exist.
- Did it authenticate? Send a reset to an address you control at a major mailbox provider and read the raw headers.
Authentication-Results: mx.example.com;
spf=pass smtp.mailfrom=bounces.emails.sh;
dkim=pass header.d=acme.com;
dmarc=pass header.from=acme.comIf any of those says fail, stop and fix that. Nothing about your subject line matters while DMARC is failing.
The authentication layer
Most reset mail in spam is unaligned rather than unauthenticated. SPF passes for the provider's bounce domain, DKIM is signed by someone else's domain, and DMARC therefore does not pass alignment with the From address the user sees. Receivers treat a message claiming to be from your brand, without a signature from your brand, as exactly what it looks like.
The fix is publishing your own DKIM entries so header.d matches your From domain, and then publishing DMARC so receivers know what to do. That whole sequence is in SPF, DKIM, and DMARC explained for developers.
If you are sending through a personal mailbox, that is the cause and no amount of tuning will fix it, per why Gmail SMTP fails in production.
The reputation layer
Assume authentication is clean and the mail still lands in spam. The next question is what else your domain sends.
Reputation is shared across everything sent from a domain. A marketing campaign to a purchased list, a re-engagement blast to addresses that have not opened anything in two years, or a support tool sending from the same domain will all pull down the placement of your reset mail. Split the streams onto separate subdomains and the reset stream keeps its own, much better, reputation: transactional email versus marketing email.
Also check that you are suppressing addresses that already bounced. Repeatedly mailing dead addresses is one of the strongest negative signals available, and it is entirely self-inflicted: bounces, complaints, and suppression lists.
The content layer
Filters do read the message. Reset mail commonly trips several signals at once.
| Pattern | Why it hurts | Instead |
|---|---|---|
| "URGENT: Your account will be locked" | Classic phishing register | "Reset your Acme password" |
| A single image with the button inside it | No text to classify, common in fraud | Real HTML text and a text link |
| Link text that hides the destination | Mismatch between anchor and href is scored | Show the URL as well as the button |
| A link shortener or a tracking redirect | Shorteners have terrible reputation | Link to your own domain |
| HTML with no plain text alternative | Multipart is expected | Always send text alongside html |
| A From address on a different domain to the link | Looks like a redirect through a compromised host | Same domain for sender and links |
| Lots of unrelated marketing footer | Dilutes a transactional message | One purpose per message |
Reset mail should be short, plain, specific about what was requested, and honest about what to do if it was not them.
Subject: Reset your Acme password
Someone asked to reset the password for your Acme account
(you@example.com).
Choose a new password:
https://acme.com/reset?token=...
This link expires in 30 minutes and can be used once.
If you did not ask for this, you can ignore this message. Your
password has not changed. Questions: support@acme.comNaming the account address in the body helps a genuine recipient and gives a phishing target one more mismatch to notice.
The infrastructure layer
Two things at this level are worth checking.
Sending IP and domain history. A brand new domain has no reputation, and mail from it is treated cautiously for the first few weeks. If you launched last Tuesday, part of what you are seeing is newness. Ramp volume gradually rather than sending a large batch on day one.
Link domain reputation. Filters evaluate the domains you link to as well as the domain you send from. If your reset link points at a shared preview URL, a shortener, or a tracking domain with a bad history, the message inherits it. Point reset links at your own verified domain, always.
Timing matters more than for other mail
A reset is useless if it arrives after the user gives up, which is roughly ninety seconds. Two things cause the delay:
Queueing behind bulk. If a campaign is in flight on the same sending stream, your reset can sit behind it. Separate streams solve this too.
Doing work before the send. Rendering, database writes, and analytics in the request path all add latency. Generate the token, send, then do the rest.
Check your own timing by comparing the send timestamp against the delivery event:
curl -s https://emails.sh/v1/emails/em_2t9x4k1c7v \
-H "Authorization: Bearer $EMAILSSH_API_KEY"The order to fix things
- Authentication, until DMARC passes with your own domain in
header.d. - Stream separation, so campaigns cannot drag reset mail down.
- Suppression, so you stop mailing dead addresses.
- Content, which is the part everyone starts with and the least important.
- Timing, so the message is useful when it arrives.
Most teams do this list backwards, rewrite the copy twice, and change nothing, because the DKIM signature was wrong the whole time.
Questions
- Why does my password reset go to spam but my receipts do not?
- Receipts get opened and rarely reported, so they accumulate positive engagement. Reset mail is infrequent, urgent in tone, and structurally similar to phishing, so it is scored more harshly with less positive history behind it.
- Does adding an unsubscribe link help a reset email?
- No, and it can hurt. Reset mail is transactional and should not carry unsubscribe controls, because honouring an unsubscribe would lock the user out of their own account.
- How fast should a password reset email arrive?
- Under a minute, ideally within a few seconds. Users who wait longer request another one, which creates duplicate tokens and support tickets. Sending on a separate stream from bulk mail is the single biggest factor.
- Is a six-digit code better than a reset link?
- For deliverability, slightly: a code has no link for a filter to evaluate and no link scanner to consume it. For usability it depends on the device. Many products send both in the same message.
Give your agent an address it can answer from.
Create an inbox