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.

  1. Did it send? Look up the message id in your delivery log. If your code never called the API, everything below is irrelevant.
  2. 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.
  3. 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.com

If 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.

PatternWhy it hurtsInstead
"URGENT: Your account will be locked"Classic phishing register"Reset your Acme password"
A single image with the button inside itNo text to classify, common in fraudReal HTML text and a text link
Link text that hides the destinationMismatch between anchor and href is scoredShow the URL as well as the button
A link shortener or a tracking redirectShorteners have terrible reputationLink to your own domain
HTML with no plain text alternativeMultipart is expectedAlways send text alongside html
A From address on a different domain to the linkLooks like a redirect through a compromised hostSame domain for sender and links
Lots of unrelated marketing footerDilutes a transactional messageOne 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.com

Naming 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:

Shell
curl -s https://emails.sh/v1/emails/em_2t9x4k1c7v \
  -H "Authorization: Bearer $EMAILSSH_API_KEY"

The order to fix things

  1. Authentication, until DMARC passes with your own domain in header.d.
  2. Stream separation, so campaigns cannot drag reset mail down.
  3. Suppression, so you stop mailing dead addresses.
  4. Content, which is the part everyone starts with and the least important.
  5. 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