Friday, October 16, 2026 New tutorials published every day
Home
Tutorials
Core PHP PHP Course JavaScript React & Next.js Python WordPress Artificial Intelligence
Course
Lesson 1 — Start Here Course playlist Lesson notes & code
Projects
PHP Projects — all projects
More
About Contact Search Login
Core PHP

How to Hash Passwords in PHP: password_hash() and password_verify() Explained

Learn how to hash passwords in PHP the right way using password_hash() and password_verify(). Simple code examples, database setup, login check and common mistakes.

How to Hash Passwords in PHP: password_hash() and password_verify() Explained

To hash a password in PHP, use the built-in function password_hash() when a user registers, save the hash in your database, and check the password at login with password_verify(). Never store passwords as plain text, and never use old methods like MD5 or SHA1 for passwords. PHP's built-in functions are secure, simple, and need only a few lines of code.

Why Password Hashing Matters

Imagine your website stores passwords exactly as users type them. If anyone ever gets access to your database — through a leak, a mistake, or an attack — every single password is exposed. And because most people reuse the same password on many sites, the damage does not stop at your website.

Hashing solves this problem. A hash turns a password into a long, fixed-length string of random-looking characters. The process works in one direction only: you can turn a password into a hash, but you cannot turn the hash back into the password. When a user logs in, you hash what they typed and compare it with the saved hash. If they match, the password is correct, yet the real password was never stored anywhere.

Why Not MD5 or SHA1?

Old tutorials still show md5() and sha1() for passwords. Please do not use them. MD5 and SHA1 were designed to be very fast, and fast is exactly what you do not want for passwords. A modern computer can try billions of MD5 hashes per second, so weak passwords fall quickly.

PHP's password_hash() uses strong algorithms such as bcrypt (the default) and Argon2id. These are deliberately slow and include a random "salt" for every password, so two users with the same password still get completely different hashes. That one detail defeats most common cracking tricks.

Step 1: Create the Users Table

Your password column must be long enough for the hash. A bcrypt hash is 60 characters, but future algorithms may produce longer hashes, so use VARCHAR(255):

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(100) NOT NULL UNIQUE,
  password VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

If you are new to database work in PHP, first read our guide on how to connect PHP with a MySQL database, which covers both MySQLi and PDO.

Step 2: Hash the Password at Registration

When a user submits your registration form, hash the password before saving it:

require "db.php"; // your PDO connection

$name = $_POST["name"];
$email = $_POST["email"];
$plainPassword = $_POST["password"];

// Create the hash
$hash = password_hash($plainPassword, PASSWORD_DEFAULT);

$stmt = $pdo->prepare("INSERT INTO users (name, email, password) VALUES (:name, :email, :password)");
$stmt->execute([
    "name" => $name,
    "email" => $email,
    "password" => $hash
]);

echo "Registration successful!";

That is all. PASSWORD_DEFAULT tells PHP to use the best algorithm available in your PHP version — currently bcrypt. As PHP improves, the default may change to something even stronger, and your code will use it automatically for new passwords.

If you specifically want Argon2id, the strongest option on most modern servers, you can write:

$hash = password_hash($plainPassword, PASSWORD_ARGON2ID);

Step 3: Verify the Password at Login

At login, you do not hash and compare strings yourself. Fetch the user by email, then let password_verify() do the checking:

session_start();
require "db.php";

$email = $_POST["email"];
$plainPassword = $_POST["password"];

$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute(["email" => $email]);
$user = $stmt->fetch();

if ($user && password_verify($plainPassword, $user["password"])) {
    $_SESSION["user_id"] = $user["id"];
    $_SESSION["username"] = $user["name"];
    echo "Login successful. Welcome, " . $user["name"];
} else {
    echo "Invalid email or password.";
}

Notice the error message: "Invalid email or password." It does not say which one was wrong. That is deliberate — it stops attackers from learning which email addresses are registered on your site.

This login flow is exactly what powers projects like our student registration and login system, where registration, hashing and sessions work together.

Step 4: Keep Hashes Up to Date

Algorithms improve over time. PHP gives you password_needs_rehash() to check, at login, whether a saved hash still meets your current standard. If it does not, you simply create a fresh hash from the password the user just typed and save it:

if (password_verify($plainPassword, $user["password"])) {
    if (password_needs_rehash($user["password"], PASSWORD_DEFAULT)) {
        $newHash = password_hash($plainPassword, PASSWORD_DEFAULT);
        $stmt = $pdo->prepare("UPDATE users SET password = :password WHERE id = :id");
        $stmt->execute(["password" => $newHash, "id" => $user["id"]]);
    }
    // continue login...
}

The user notices nothing, and your password security quietly upgrades itself over time.

Common Mistakes to Avoid

  • Storing plain-text passwords. The single worst mistake. There is never a good reason for it.
  • Using MD5, SHA1 or a "secret" custom formula. Attackers know these tricks. Use the built-in functions.
  • Hashing twice or trimming the password first. Hash exactly what the user typed, once, and save the full hash.
  • A password column that is too short. If your column cuts the hash, verification will always fail. Use VARCHAR(255).
  • Comparing hashes with == yourself. Always use password_verify(); it compares safely and reads the algorithm and salt from the hash itself.
  • Emailing users their password. If you can send a user their password, you stored it wrongly. Send a reset link instead.

Frequently Asked Questions

What is the best hashing algorithm for passwords in PHP?

Use PASSWORD_DEFAULT in password_hash(). It currently means bcrypt and will move to stronger algorithms in future PHP versions. Argon2id is also an excellent choice where it is available.

Can a hashed password be decrypted?

No. Hashing is a one-way process. That is the whole point — even you, the website owner, cannot see a user's real password. Login works by checking a new input with password_verify(), not by unlocking the stored hash.

Why is my password_verify() always returning false?

The most common causes are a database column that is too short and cut the hash, extra spaces added while saving, or hashing the password again before verifying. Check the column size first; it should be VARCHAR(255).

Do I need to add my own salt?

No. password_hash() creates a strong random salt automatically for every password and stores it inside the hash. Adding your own salt can actually make things weaker or break verification.

Is password_hash() enough to make a login system safe?

It is the most important part, but not the only part. Also use prepared statements, HTTPS, sensible session handling, and protection against repeated login attempts. Our guide on how websites get hacked using SQL injection explains the other side of the story.

Conclusion

Password security in PHP is genuinely simple when you use the right tools: hash with password_hash() at registration, check with password_verify() at login, store hashes in a VARCHAR(255) column, and let PHP keep the algorithm modern for you. Skip MD5 and SHA1 forever. Build this habit into every project from day one, and your users' accounts will be far safer. Follow Coding Cush for one simple, practical coding guide every day.

L
Written by

Lavkush K

Lav is a software developer with 5+ years of experience in the software industry, specialising in PHP and MySQL web development. Over the years he has built business websites, WordPress platforms and complete web applications — admin panels, login systems, dashboards and more. Through CodingCush, he turns that real project experience into simple, step-by-step tutorials with full working code, so students and beginners can learn by building real things, not just by reading theory.

0 Comments

Leave a Comment