Of all the emails your application sends, password reset emails carry the highest stakes. Get them wrong, and you've created a security vulnerability that attackers will exploit. Get them right, and you've built a secure, user-friendly recovery flow that builds trust.
Password reset emails are also among the most time-sensitive. A user who's locked out of their account is frustrated and anxious. Every minute they wait for the reset email is a minute they might give up, contact support, or lose trust in your product.
The security fundamentals
Password reset emails are a prime target for attackers because they provide a path to account takeover. Your reset flow needs to be secure by design.
Never send passwords via email. Not temporary passwords, not generated passwords, not anything. Email is not a secure channel—it can be intercepted, forwarded, or accessed by anyone with access to the inbox. Always use a secure reset link that lets users set their own password.
Reset tokens must be cryptographically random and unguessable. Use your platform's secure random number generator, not predictable values like timestamps or sequential IDs. A token should have at least 128 bits of entropy.
Tokens should be short-lived. 15 minutes to 1 hour is reasonable. Longer expiration windows give attackers more time to intercept or guess tokens. Users who don't reset within the window can request a new token.
Tokens should be single-use. Once a token is used to reset a password, invalidate it immediately. This prevents replay attacks where an attacker uses an intercepted token after the legitimate user.
Store tokens securely. Hash tokens before storing them in your database, just like passwords. If your database is compromised, attackers shouldn't be able to use stored tokens.
The user experience
Security and usability aren't opposites—a confusing reset flow leads to support tickets and workarounds that create their own security problems.
Send reset emails immediately. Users are waiting. A reset email that takes 10 minutes to arrive feels broken. Prioritize reset emails in your sending queue—they should go out within seconds.
Make the reset link impossible to miss. A big, obvious button that says 'Reset Password' works better than a text link buried in a paragraph. Users are scanning for the action, not reading carefully.
Keep the email focused. This isn't the time for promotional content, social links, or elaborate design. The user has one goal: reset their password. Everything else is a distraction.
Include context about the request. When was it requested? From what IP or location? This helps users identify if the request was legitimate or if someone else is trying to access their account.
Provide a clear path if they didn't request the reset. 'If you didn't request this, you can ignore this email' is reassuring. Some users will receive reset emails they didn't request (typos, malicious attempts) and need to know they're safe.
The reset flow
The complete reset flow has several steps, each with security implications:
The request page should ask only for an email address. Don't confirm whether the address exists in your system—this leaks information to attackers trying to enumerate accounts. Always show the same message: 'If an account exists with this email, we've sent reset instructions.'
Rate limit reset requests. An attacker shouldn't be able to flood an email address with reset emails (harassment) or try many addresses quickly (enumeration). Limit requests per email and per IP.
The reset page (where users land after clicking the link) should validate the token before showing the password form. If the token is invalid or expired, show a clear error and let them request a new one.
After successful reset, invalidate the token, invalidate all other sessions for that account (the user might be resetting because they suspect compromise), and send a confirmation email notifying them that their password was changed.
The confirmation email is a security feature. If someone else reset the password, the legitimate user will see this email and know their account was compromised. Include information about how to recover the account if this wasn't them.
Common mistakes
Even experienced developers make mistakes with password reset flows:
Predictable tokens are surprisingly common. Using the user ID, email hash, or timestamp as the token (or part of it) makes tokens guessable. Always use cryptographically secure random values.
Not expiring tokens leaves a window open indefinitely. If a user requests a reset but doesn't complete it, that token shouldn't work forever. Implement expiration.
Not invalidating tokens after use allows replay attacks. Once a token is used, it should never work again, even if it hasn't expired yet.
Confirming email existence helps attackers. If your reset page says 'no account found' for invalid emails, attackers can use this to build a list of valid accounts. Always show the same response.
Not notifying after reset leaves users unaware of potential compromise. The confirmation email is a critical security control, not just a nice-to-have.
Slow delivery frustrates users into insecure workarounds. If reset emails are slow, users might try multiple times, contact support for manual resets, or give up entirely. Speed matters.
The email content
The reset email itself should be simple and clear:
Subject line: Keep it straightforward. 'Reset your password' or 'Password reset request' works. Avoid urgency tactics that look like phishing.
Sender: Use a recognizable sender name and address. 'YourApp <[email protected]>' is better than '[email protected]'. Users should immediately recognize who the email is from.
Body: Explain what happened ('You requested a password reset'), provide the reset button/link, mention the expiration ('This link expires in 1 hour'), and explain what to do if they didn't request it.
Don't include the username or other account details. If the email is intercepted, you don't want to give attackers more information than necessary.
Consider including the request context (IP, location, time) so users can verify the request was legitimate. But balance this against privacy concerns—some users don't want their location in email.
Frequently asked questions
How long should reset tokens be valid?
15 minutes to 1 hour is typical. Shorter is more secure but may frustrate users who don't check email immediately. 1 hour is a reasonable balance for most applications.
Should I require the old password to set a new one?
No—the whole point of password reset is that the user doesn't know their current password. The reset token serves as proof of identity. Requiring the old password would defeat the purpose.
What if the user's email is compromised?
Password reset via email assumes the email account is secure. If it's not, the attacker can reset passwords for any linked accounts. Consider offering alternative recovery methods (SMS, backup codes, security questions) for high-security applications.
Should I log users in automatically after reset?
Opinions vary. Automatic login is more convenient but means anyone with the reset link gets access. Requiring login after reset adds friction but ensures the user knows their new password works. For high-security applications, require login.