Why Password Resets Are Unique
Password reset emails differ from other transactional emails in several ways:| Factor | Impact |
|---|---|
| Time-sensitive | Users expect delivery within seconds, not minutes |
| Security-critical | The email contains a credential-equivalent token |
| High-engagement | Users actively check their inbox, so deliverability is rarely a perception problem — but failures are immediately noticed |
| Single-use | The token should only work once and expire quickly |
| Triggered by the user | The recipient explicitly requested this email, so spam complaints are rare |
Token Security
The reset token is the most important part of the email. A weak or poorly managed token can let attackers take over accounts.Generate Cryptographically Secure Tokens
Use your language’s cryptographically secure random generator — neverMath.random() or similar predictable sources:
Set Short Expiry Times
Reset tokens should expire quickly. The user just requested the email, so they’re actively waiting for it:| Expiry | Recommendation |
|---|---|
| 15–60 minutes | Recommended range |
| 30 minutes | Good default |
| 24 hours | Too long — increases the window for token interception |
Invalidate Tokens After Use
A token should become invalid the moment it’s used. Also invalidate any existing tokens when a new reset is requested:Email Content
Keep the Email Simple
A password reset email should contain exactly what the user needs and nothing more:Acknowledge the request
A brief statement confirming that a password reset was requested for their account.
Example HTML Structure
What Not to Include
- Don’t include the user’s password — not even a temporary one. Always use a link to a secure reset form.
- Don’t include the username — if the email is intercepted, this reveals the account identifier.
- Don’t include marketing content — this is a transactional email. Adding promotions risks spam classification and violates CAN-SPAM regulations.
- Don’t include account details beyond the email address — minimize information exposure.
Sending with Lettr
Use a
from address on a subdomain (like mail.yourdomain.com) rather than your root domain. This isolates your transactional sending reputation from any marketing email you may send. See Subdomain vs Root Domain for details.Delivery Speed
Password resets are one of the few email types where delivery speed is immediately noticeable. Users are staring at their inbox waiting.Optimize for Fast Delivery
- Send immediately — don’t queue password reset emails behind bulk sends. Use a separate sending priority or queue if your system batches emails.
- Keep the email small — avoid large images, heavy templates, or excessive HTML. A lightweight email processes faster at every stage of the delivery pipeline.
- Use a reputable sending domain — domains with established reputation are processed faster by receiving servers. See Sending Reputation.
- Monitor delivery time — use Lettr webhooks to track the time between your API call and the
email.deliveredevent. Alert if this exceeds a few seconds.
Handling Edge Cases
User Doesn’t Receive the Email
Provide a “Resend” button on your password reset page, but rate-limit it:- Allow a maximum of 3 reset emails per email address per hour
- Show a generic message regardless of whether the email exists in your system (prevents account enumeration)
Expired Token
When a user clicks an expired link, show a clear message and offer to send a new one:- Don’t just say “invalid link” — tell them the link has expired
- Provide a button to request a new reset email
- Don’t auto-send a new email without user action
Reset from an Unrecognized Device
Consider sending a follow-up notification after a successful password change:Common Mistakes
Using predictable or sequential tokens
Using predictable or sequential tokens
Tokens generated with
Math.random(), auto-incrementing IDs, or timestamps are guessable. Always use crypto.randomBytes() or your language’s equivalent CSPRNG.Not hashing stored tokens
Not hashing stored tokens
If you store raw tokens in your database, a database breach exposes all pending resets. Hash tokens before storage using SHA-256 or bcrypt.
Token doesn't expire or expires too late
Token doesn't expire or expires too late
A 24-hour or never-expiring token gives attackers a large window. Use 30–60 minutes as the default.
Sending the password in the email
Sending the password in the email
Never email a password — temporary or otherwise. Always send a link to a form where the user sets a new password over HTTPS.
Revealing account existence
Revealing account existence
If your reset endpoint returns different responses for existing vs non-existing emails, attackers can enumerate your user base. Always return the same message.
Adding marketing content to the email
Adding marketing content to the email
Password reset emails are transactional. Adding promotional content can trigger spam filters and may violate CAN-SPAM. Keep the email focused.