Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
📘 Lesson  ·  Lesson 47

Forgot Password Reset in PHP

In this lesson: Secure forgot-password flow in PHP — token, expiry, email reset link and new password update — full code.

How password reset works

Since passwords are stored only as hashes, you cannot "look up" a forgotten one. Instead, a forgot-password flow proves the user controls their email and lets them set a new password. It uses a one-time token: request → email a link → verify token → set new password.

💡 Never email the real password. Email a secure, expiring token link instead.

Step 1: Request a reset

<?php
  $email = trim($_POST["email"]);
  // check the email exists in users table (prepared statement)
  $stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
  $stmt->execute([$email]);
  $user = $stmt->fetch();
?>

The user submits their email. We look it up with a prepared statement. If it exists, we move on to create a reset token.

Store the token with expiry

<?php
  $token = bin2hex(random_bytes(32));   // secure random token
  $expiry = date("Y-m-d H:i:s", time() + 3600);  // 1 hour

  $stmt = $pdo->prepare(
      "UPDATE users SET reset_token = ?, reset_expiry = ? WHERE email = ?"
  );
  $stmt->execute([$token, $expiry, $email]);
?>

We generate a long random token with random_bytes and save it in the database with a one-hour expiry. Storing it server-side lets us verify it later.

<?php
  $link = "https://yoursite.com/reset.php?token=$token";
  $body = "Click to reset your password (valid 1 hour):\n$link";
  // send $body by email (PHPMailer / SMTP)
?>

We email a link containing the token. When the user clicks it, they land on the reset page with the token in the URL, ready to be verified.

Step 2: Set a new password

<?php
  $token = $_GET["token"] ?? "";
  $stmt = $pdo->prepare(
      "SELECT id FROM users WHERE reset_token = ? AND reset_expiry > NOW()"
  );
  $stmt->execute([$token]);
  $user = $stmt->fetch();

  if ($user) {
      $newHash = password_hash($_POST["password"], PASSWORD_DEFAULT);
      $upd = $pdo->prepare(
          "UPDATE users SET password = ?, reset_token = NULL WHERE id = ?"
      );
      $upd->execute([$newHash, $user["id"]]);
      echo "Password updated";
  } else {
      echo "Invalid or expired link";
  }
?>
Password updated

We accept the token only if it matches and has not expired (reset_expiry > NOW()). The new password is hashed with password_hash, and the token is cleared so it cannot be reused.

Common mistakes

  • Emailing the actual password. You cannot — it is only a hash. Email a token link.
  • Weak tokens. Use random_bytes, not rand() or md5(time()).
  • No expiry. Always set and check an expiry on the token.
  • Not clearing the token. Remove it after use so the link cannot be reused.

Summary

  • You cannot recover a hashed password — you reset it via a token.
  • Generate a secure token with random_bytes and store it with an expiry.
  • Email a link containing the token; never email the password.
  • Verify the token and expiry, then set a new hashed password.
  • Clear the token after use so the link is one-time.

Next, understand hashing in password hashing, or build the login in login and registration.

Frequently Asked Questions

How does a forgot-password flow work?
The user asks for a reset by email. You create a random token, store it with an expiry, and email a link containing it. When the user clicks the link, you verify the token and let them set a new password, which you hash before saving.
Why use a token instead of emailing the password?
You should never know or email the real password — it is stored only as a hash. A one-time token proves the user controls the email and lets them set a new password securely, without ever exposing the old one.
Why must the reset token expire?
A token that never expires stays a risk if the email is later seen by someone else. Giving it a short life (say one hour) limits the window an attacker has to use a leaked link.
How do I generate a secure token?
Use bin2hex(random_bytes(32)) for a long, unpredictable token. Avoid weak functions like rand() or md5(time()), which are guessable.
Should the new password be hashed?
Yes. When the user sets a new password, hash it with password_hash() before saving, exactly like at signup. The database should never hold a plain password.
← Back to PHP Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

💻 Live Code Editor

This page's programs are ready here — run them, edit them, and learn. No installation needed.
Powered by OneCompiler. The code loads into the editor automatically — press Run to see the output. If the editor does not open, open it in a new tab.
Next topicCalculator (Switch Case) Your progress: 47 / 50 (94%) · Lesson 48 / 50 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free