PHP Password Verifier
Check a plaintext password against a PHP password_hash() bcrypt digest — entirely in your browser.
Checked entirely in your browser using bcryptjs — neither the password nor the hash is ever sent anywhere.
How PHP password verification works
PHP's password_hash() function (with the default or PASSWORD_BCRYPT algorithm) produces a self-contained bcrypt digest such as $2y$10$eImiTXuWVxfM37uY4JANjQZ4G3hSuXCJq1... The string encodes the algorithm identifier ($2y$), the cost factor (10, meaning 210 rounds), the 22-character salt, and finally the hash itself — all separated by $. There is deliberately no way to reverse a bcrypt hash back into the original password; verification instead re-hashes the candidate password using the salt and cost factor already embedded in the stored hash, then compares the two digests.
This tool performs that same comparison locally with the bcryptjs library's compareSync(), which is compatible with PHP's $2y$ hashes as well as the generic $2a$/$2b$variants used by other languages. It mirrors exactly what PHP's password_verify($password, $hash) does on the server — useful for debugging login issues, checking seed data, or confirming a hash was generated correctly — without ever needing a PHP runtime or sending either value over the network.
Built and maintained by Meet Shah · Last updated
What this tool is used for
- Checking a stored bcrypt hash against a candidate password while debugging a login.
- Confirming a migrated hash still verifies after a database move.
- Testing that a hash produced elsewhere is compatible with PHP's verify function.
- Checking whether a login failure is the hash or the surrounding code.
- Reading the cost factor out of an existing hash.
Frequently Asked Questions
- What does the $2y$ prefix mean?
- It identifies PHP's own bcrypt variant. `$2a$` was the original, `$2b$` the fixed OpenBSD version, and `$2y$` was introduced by PHP in 2011 after a bug in its own implementation — so the prefix records which implementation produced the hash.
- Why does the same password produce a different hash every time?
- Because bcrypt generates a random salt per hash and stores it in the output string. That is what makes precomputed rainbow tables useless, and it is why verification has to run the algorithm rather than compare strings.
- What is the cost factor?
- The number after the prefix — a base-2 logarithm of the iteration count, so cost 12 is 4,096 rounds and each increment doubles the work. PHP's default has risen over time, and the guidance is to pick the highest cost your server tolerates at around 100ms per hash.
- What is bcrypt's 72-byte limit?
- Anything past 72 BYTES of the password is silently ignored. That is bytes, not characters, so a passphrase in a non-Latin script hits it far sooner than it looks — and pre-hashing with SHA-256 before bcrypt is the standard workaround.
- Should new code still use bcrypt?
- It remains acceptable, and Argon2id is the current recommendation where available — PHP exposes it as `PASSWORD_ARGON2ID`. Using `PASSWORD_DEFAULT` plus `password_needs_rehash` lets the algorithm move forward without a migration script.
Common errors and gotchas
- Comparing hashes with string equality, which fails because each hash has its own salt.
- Assuming a truncated hash column still verifies, when bcrypt hashes need their full length.
- Overlooking PHP's 72-byte truncation, which silently ignores a longer passphrase's tail.
- Testing a real production password in any tool, which is a habit worth not having.
- Confusing the algorithm prefix, where `$2y$` and `$2a$` come from different eras.