Anatomy of a bcrypt hash
$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
\/ \/ \____________________/\_____________________________/
| | | |
| cost salt (22 chars) hash (31 chars)
version
- Version (
2b): which revision of the algorithm made the hash. - Cost (
12): the work factor. bcrypt runs 2^cost rounds of its key setup, so cost 12 means 4,096 rounds. - Salt: 16 random bytes in bcrypt’s own Base64 alphabet (
./A–Za–z0–9), unique per hash. - Hash: the 23-byte result. Everything needed to verify a password is in the string, so you store it in a single column (
CHAR(60)).
Using bcrypt in your code
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 12);
const ok = await bcrypt.compare(password, hash); // true or false
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
ok = bcrypt.checkpw(password.encode(), hashed)
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
$ok = password_verify($password, $hash);
if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
// store password_hash($password, ...) again
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), 12)
err = bcrypt.CompareHashAndPassword(hash, []byte(password)) // nil if it matches
htpasswd -nbBC 12 alice 'secret' # prints alice:$2y$12$...
The hashing here runs in your browser with bcrypt.js, so passwords are never sent anywhere. Need a strong password to hash? Use the password generator. Looking for MD5 or SHA-256 instead? Those are in the hash generator.
Frequently asked questions
What cost factor should I use for bcrypt?
Pick the highest cost that keeps hashing around 250 ms on your production server. In 2026 that is usually 12 (OWASP recommends at least 10). Each step doubles the time: cost 13 takes twice as long as 12. Re-check every few years, and rehash on login when a user’s stored hash has a lower cost than your current setting.
Why does the same password give a different hash every time?
Each hash includes a new random 16-byte salt, stored in characters 8 to 29 of the hash. Two users with the same password get different hashes, so attackers can’t use precomputed tables. To check a password, don’t hash it again and compare strings. Use your library’s compare or verify function, which reads the salt and cost from the stored hash.
What is the difference between $2a$, $2b$ and $2y$?
They mark revisions of the algorithm. $2a$ is the classic version. $2b$ (2014) fixes a bug in OpenBSD’s implementation with passwords longer than 255 bytes. $2y$ is PHP’s marker for its corrected implementation and is equivalent to $2b$. For normal passwords all three produce the same result, and this tool verifies all of them.
Is there a maximum password length?
Yes. bcrypt only uses the first 72 bytes of the password; the rest is ignored. With UTF-8, that can be fewer than 72 characters (an emoji is 4 bytes). The tool warns when a password is longer. If you need to support longer passwords, the safe approach is to pre-hash with HMAC-SHA256 and Base64-encode the result before bcrypt, or use Argon2id instead.
bcrypt or Argon2?
Both are fine for password storage. OWASP lists Argon2id first because it is memory-hard, which makes GPU attacks more expensive, and it has no 72-byte limit. bcrypt is older, available in every language and framework, and still a solid choice at cost 10 or higher. Avoid MD5, SHA-1 and plain SHA-256 for passwords; they are designed to be fast.