Question

How to Hash Passwords with bcrypt in PHP

phppasswordscryptographypassword-protectionbcrypt

Question

I often hear the advice: “Use bcrypt for storing passwords in PHP.”

What exactly is bcrypt, and how is it used in PHP?

PHP does not appear to provide a function literally named bcrypt, and many search results mention Blowfish or old encryption libraries. Since Blowfish is a reversible cipher, that seems confusing when passwords should be stored with a one-way hash, not encrypted in a way that can be decrypted later.

So how does bcrypt relate to password storage in PHP, and what is the correct way to hash and verify passwords?

Short Answer

By the end of this page, you will understand what bcrypt is, why it is suitable for password hashing, and how PHP uses it through password_hash() and password_verify(). You will also learn the difference between hashing and encryption, how salts and cost factors work, and how to avoid common password-storage mistakes.

Concept

bcrypt is a password hashing algorithm designed specifically for storing passwords securely.

Unlike general-purpose hashing functions such as MD5 or SHA-1, bcrypt is intentionally slow. That slowness is a feature: it makes brute-force attacks much more expensive for an attacker.

Why bcrypt matters

When a user creates an account, you should not store the plain password. You also should not “encrypt” it in a reversible way. Instead, you store a hash of the password.

A password hash should have these properties:

  • One-way: you can verify a password, but you cannot recover the original password from the hash.
  • Salted: each password hash includes a random salt so identical passwords do not produce identical stored values.
  • Adaptive: the algorithm should allow you to increase its work factor over time as hardware gets faster.

bcrypt provides these properties.

bcrypt and Blowfish

The confusion often comes from the fact that bcrypt was built using ideas from the Blowfish cipher, but bcrypt itself is not just “encrypting the password with Blowfish.” It is a separate password-hashing scheme.

So while Blowfish is a cipher and reversible when used for encryption, bcrypt is a password hashing function designed for one-way password storage.

How PHP uses bcrypt

In modern PHP, you normally do not call a function named bcrypt directly. Instead, you use:

password_hash($password, PASSWORD_BCRYPT)

and verify with:

password_verify($password, $storedHash)

Even better, many developers use:

password_hash($password, PASSWORD_DEFAULT)

This lets PHP choose a safe default algorithm for the current version, which may be bcrypt or another stronger algorithm in the future.

Important idea

For password storage, the workflow is:

  1. User enters a password.
  2. PHP hashes it with password_hash().
  3. You store the resulting hash string in the database.
  4. Later, when the user logs in, you call password_verify().
  5. PHP checks whether the entered password matches the stored hash.

At no point do you decrypt anything.

Mental Model

Think of password hashing like sealing a word into a special machine that produces a unique stamp.

  • You put in the password.
  • The machine creates a stamp string.
  • You store the stamp.
  • Later, if someone gives you a password, you run it through the same type of machine and check whether it matches.

But bcrypt’s machine has two important upgrades:

  • It adds a random ingredient called a salt, so the same password does not always create the same output.
  • It works deliberately slowly, like a security gate that takes a little time to process each guess.

That delay is tiny for one login, but huge for an attacker trying millions of guesses.

Take Quiz

Syntax and Examples

Core syntax

Hash a password

<?php
$password = 'MySecurePassword123!';
$hash = password_hash($password, PASSWORD_BCRYPT);

echo $hash;

Verify a password

<?php
$enteredPassword = 'MySecurePassword123!';
$storedHash = '$2y$10$abcdefghijklmnopqrstuuK5Kx1ZQxVZJz4pA7Yl0QWk4rWkV8wQy';

if (password_verify($enteredPassword, $storedHash)) {
    echo 'Password is correct';
} else {
    echo 'Invalid password';
}

Recommended modern approach

<?php
$password = 'MySecurePassword123!';
$hash = password_hash($password, PASSWORD_DEFAULT);

PASSWORD_DEFAULT is usually the safest choice because PHP can update the algorithm over time.

Example: registration and login

Registration

<?php
$password = $_POST['password'];
$hash = password_hash($password, PASSWORD_DEFAULT);

// Store $hash in the database

Login

<?php
$password = $_POST['password'];
$storedHash = $row['password_hash']; // loaded from database

if (password_verify($password, $storedHash)) {
    echo 'Login successful';
} else {
    echo 'Login failed';
}

Cost option

bcrypt allows a cost value, which controls how much work the hash takes.

<?php
$hash = password_hash('secret123', PASSWORD_BCRYPT, ['cost' => 12]);

A higher cost is slower and more secure against brute force, but too high can hurt performance. Choose a value your server can handle comfortably.

Step by Step Execution

Consider this example:

<?php
$password = 'hello123';
$hash = password_hash($password, PASSWORD_BCRYPT);

if (password_verify('hello123', $hash)) {
    echo 'Match';
} else {
    echo 'No match';
}

Step by step

1. The password is stored in a variable

$password = 'hello123';

At this moment, it is plain text in memory.

2. PHP creates a bcrypt hash

$hash = password_hash($password, PASSWORD_BCRYPT);

PHP does several things internally:

  • generates a random salt
  • combines the password and salt
  • runs the bcrypt algorithm
  • returns a string containing the algorithm info, cost, salt, and resulting hash

The result may look like this:

$2y$10$wH8m8lK0L7mP0Ww4mA7u7O5mJmV7rYvM1nYH2R8QGQ6XgP3gX2YtK

You store that full string in the database.

3. A login attempt is verified

password_verify('hello123', $hash)

PHP reads the details from the stored hash itself:

  • which algorithm was used
  • what cost was used
  • what salt was used

Then it hashes the entered password the same way and compares the result securely.

4. The condition succeeds

Because the entered password matches the original password, password_verify() returns true, so the output is:

Match

Key point

You do not need to store the salt separately. The hash string already contains the necessary metadata.

Real World Use Cases

bcrypt is used anywhere an application needs to store user passwords securely.

Common examples

  • User registration systems in websites and web apps
  • Login systems for admin panels, dashboards, and APIs
  • Password reset flows after a user chooses a new password
  • Internal tools where staff accounts need secure authentication
  • Membership platforms such as forums, LMS apps, and e-commerce sites

Example scenarios

A blog CMS

When an editor creates an account, the CMS stores a bcrypt-based password hash in the users table.

An API backend

An API authenticates users with email and password before issuing a token. The password is verified with password_verify().

A SaaS application

When security settings improve, the app can detect outdated hashes and rehash them using password_needs_rehash() after a successful login.

Take Quiz

Real Codebase Usage

In real projects, developers usually wrap password handling in small, reusable flows rather than scattering the logic everywhere.

Common patterns

Validation before hashing

<?php
if (empty($_POST['password'])) {
    throw new InvalidArgumentException('Password is required.');
}

$hash = password_hash($_POST['password'], PASSWORD_DEFAULT);

This avoids hashing empty or invalid input.

Verify during login

<?php
if (!password_verify($password, $user['password_hash'])) {
    return 'Invalid credentials';
}

This is a common guard-clause style: fail early if the password is wrong.

Rehash when needed

<?php
if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
    $newHash = password_hash($password, PASSWORD_DEFAULT);
    // Update the database with $newHash
}

This lets an application upgrade old hashes gradually.

Storing only the hash

Real codebases typically store fields like:

  • id
  • email
  • password_hash
  • created_at

They do not store:

  • plain passwords
  • separately stored salts for password_hash() output
  • reversible encrypted passwords

Error handling and authentication flow

Typical login flow:

  1. Load the user by email or username.
  2. If the user does not exist, return a generic error.
  3. Verify the password with password_verify().
  4. If valid, create a session or issue a token.
  5. Optionally rehash the password if needed.

Common Mistakes

1. Using MD5 or SHA-1 for passwords

These algorithms are too fast for password storage.

Bad

<?php
$hash = md5($password);

Better

<?php
$hash = password_hash($password, PASSWORD_DEFAULT);

2. Trying to decrypt password hashes

A password hash is not meant to be decrypted.

Wrong idea

<?php
// There is no correct way to decrypt a password hash

Correct approach

Use password_verify() to compare a plain password to a stored hash.

3. Manually creating salts unnecessarily

Older tutorials often show manual salt handling. With password_hash(), PHP handles salt generation for you.

Unnecessary

<?php
$salt = bin2hex(random_bytes(16));
$hash = crypt($password, $salt);

Better

<?php
$hash = password_hash($password, PASSWORD_DEFAULT);

4. Comparing hashes incorrectly

Beginners sometimes hash the entered password separately and compare strings manually. That can fail because bcrypt uses a random salt.

Broken approach

<?php
if (password_hash($inputPassword, PASSWORD_BCRYPT) === $storedHash) {
    echo 'Match';
}

This usually fails because a new salt creates a different hash each time.

Correct approach

<?php
if (password_verify($inputPassword, $storedHash)) {
    echo 'Match';
}

5. Using old libraries like mcrypt for password storage

mcrypt was for encryption, not for password hashing, and it is obsolete.

6. Storing the password in plain text

Never do this.

<?php
// Very unsafe
$password = $_POST['password'];
// Store directly in database

Always hash passwords before storing them.

Comparisons

ConceptPurposeReversible?Good for passwords?PHP tool
bcryptPassword hashingNoYespassword_hash()
EncryptionProtect readable data that must be recovered laterYesNoopenssl_encrypt()
MD5Fast general hashNoNomd5()
SHA-1 / SHA-256 aloneFast general hashNoNot recommended alonehash()
password_hash() with PASSWORD_DEFAULTSafe password hashing APINoYespassword_hash()

bcrypt vs encryption

  • Use bcrypt when you need to verify a password later.
  • Use encryption when you need to recover the original data later.

Passwords should normally be hashed, not encrypted.

PASSWORD_BCRYPT vs PASSWORD_DEFAULT

OptionWhen to useBenefit
PASSWORD_BCRYPTWhen you explicitly want bcryptPredictable algorithm
PASSWORD_DEFAULTUsually preferred in new codeLets PHP upgrade the default algorithm over time

password_verify() vs manual comparison

ApproachResult
password_verify($password, $hash)Correct and safe
password_hash($password, ...) === $hashIncorrect for bcrypt because new salts produce new hashes
Take Quiz

Cheat Sheet

Quick reference

Hash a password

$hash = password_hash($password, PASSWORD_DEFAULT);

Use bcrypt explicitly

$hash = password_hash($password, PASSWORD_BCRYPT);

Verify a password

if (password_verify($password, $storedHash)) {
    // valid
}

Rehash if needed

if (password_needs_rehash($storedHash, PASSWORD_DEFAULT)) {
    $newHash = password_hash($password, PASSWORD_DEFAULT);
}

Rules

  • Store only the hash, not the plain password.
  • Do not decrypt password hashes.
  • Do not use MD5 or SHA-1 for password storage.
  • Do not compare bcrypt hashes by re-hashing input manually.
  • Let password_hash() manage the salt.

Useful facts

  • bcrypt is intentionally slow.
  • bcrypt includes a salt.
  • the stored hash contains algorithm and cost information.
  • PASSWORD_DEFAULT is usually the best choice for new PHP code.

FAQ

What is bcrypt in PHP?

bcrypt is a password hashing algorithm. In PHP, you typically use it through password_hash() and verify with password_verify().

Does PHP have a bcrypt function?

Not usually by that exact name. PHP exposes bcrypt through the password API, especially password_hash($password, PASSWORD_BCRYPT).

Can bcrypt hashes be decrypted?

No. bcrypt is a one-way password hashing algorithm, not reversible encryption.

Why not use MD5 for passwords in PHP?

MD5 is too fast and too weak for password storage. Attackers can test huge numbers of guesses very quickly.

Should I use PASSWORD_BCRYPT or PASSWORD_DEFAULT?

For most new applications, PASSWORD_DEFAULT is preferred because PHP can move to stronger defaults in future versions.

Do I need to store the salt separately?

No. The hash generated by password_hash() already contains the salt and other metadata.

How do I check whether an old hash should be upgraded?

Use password_needs_rehash() after a successful login and save a new hash if needed.

Take Quiz

Mini Project

Description

Build a simple PHP password registration and login example that demonstrates the correct use of bcrypt-based hashing through PHP’s password API. This project shows how a password is hashed once during registration and verified later during login without ever storing the plain password.

Goal

Create a small script that hashes a password, stores the hash in a variable, and verifies login attempts correctly.

Requirements

Requirement 1 Requirement 2 Requirement 3

Take Quiz

Keep learning

Related questions

View more