Bcrypt
Generate a bcrypt hash or check a password against one, in your browser: pick the cost and the $2a$, $2b$ or $2y$ variant, and read any hash apart.
Nothing you type here leaves this browser. The work runs in a Web Worker in this tab, so open your own network panel and watch: no request goes out while it computes.
Generate a Hash
A password, a Cost and a Variant. The Salt is drawn fresh from this browser on every run, so the same password gives a different Hash each time — which is bcrypt working, not a fault.
Check a password against a Hash
A pasted Hash is read apart at once, with no password and no waiting. Add the password to answer Match. A Hash cannot be reversed, here or anywhere.
What is actually inside a bcrypt hash
A bcrypt hash is a single sixty-character string, and every part of it is in the open. Nothing about it is secret except the password that produced it, which is not in there at all. Reading one apart takes no key, no password and no computation — this page does it the moment you paste one.
- The variant, the field the string opens with: $2b$, $2a$ or $2y$. It names whose rules produced the hash.
- The cost, written as exactly two digits: 04 through 31. It is an exponent, so cost 12 means 2^12 — 4,096 — rounds of key stretching, and cost 13 is twice the work of cost 12 rather than a thirteenth more.
- The salt, twenty-two characters: sixteen random bytes, stored in the clear beside the answer they salted.
- The digest, the last thirty-one characters: twenty-three bytes of output. This is the only part that depends on the password, and it is not the hash — the hash is the whole string.
The alphabet those characters are drawn from is bcrypt's own, and it is not standard Base64: it runs ./A-Za-z0-9, so a period and a slash lead where Base64 has plus and slash trailing. Decoding one with an ordinary Base64 decoder gives the wrong bytes rather than an error, which is a classic way to lose an afternoon.
The seventy-two-byte limit is counted in bytes, and that matters
bcrypt reads at most seventy-two bytes of a password and ignores everything after them. Not seventy-two characters — seventy-two bytes of UTF-8, so how many characters that comes to depends on what a language's letters cost: the twenty-six unaccented Latin letters cost one byte apiece, and every other letter costs more.
- ASCII costs one byte per character, so an English speaker meets the limit at seventy-two characters and almost never does.
- Hebrew, Arabic, Russian and Greek cost two bytes per letter, so the limit arrives at thirty-six characters.
- Japanese, Korean and Chinese cost three, so it arrives at twenty-four.
- Most emoji cost four, and many of the ones people actually type are several code points joined together, so a handful of them is already the whole budget.
The byte counter under the password field is there so this is something you can watch approaching rather than discover afterwards. And the cut lands at the byte, not at the character, exactly as every reference implementation does it — so a multi-byte character sitting across the boundary loses part of itself and keeps the rest. That is the compatible behaviour rather than a defect, and this page says so when it happens.
Now the part that explains everything else: the truncation is invisible in the answer. The key schedule mixes exactly seventy-two bytes of key into its state and never wraps round a longer one, so hashing a hundred-byte password and hashing its first seventy-two bytes produce the identical digest. There is nothing for an implementation to detect and nothing for it to report, which is why no library raises an error and why two different long passwords that agree on their first seventy-two bytes both match the same stored hash. This page cannot refuse a long password either — if you are checking against a hash a real system produced by truncating, you need the answer — so it gives you the answer and tells you what was cut.
The variant changes the prefix and not the digest
The variant looks like a version number and is not one. The three this tool writes are not ordered, none supersedes another, and picking between them is a compatibility decision rather than a security one.
- $2b$ is what OpenBSD settled on and what Python, Node and Go emit today. It is the default here and the right answer when nothing forces another.
- $2a$ is the older one, and some long-lived Java and Spring Security deployments still expect to see it.
- $2y$ is what PHP's password_hash and therefore Laravel write, so a hash pasted into that world usually wants it.
What separates the three is how they treat a key longer than 255 bytes, and bcrypt has already stopped reading at seventy-two — so no password you can type here can reach the difference. For anything this tool will ever be given, the three produce the same twenty-three bytes and differ only in the four characters at the front. Switch the variant and the digest does not move.
Two more exist and this tool names them rather than computing them. $2x$ is not a fix: Openwall minted it to reproduce a sign-extension bug on purpose, so that hashes made by the broken code could still be checked, and implementing it here would mean putting a known defect inside the tool. $2$ is the original, from before the trailing zero byte was added to the key. Neither is emitted by anything current; if you are holding one, it came out of a system old enough that the variant is the least of what you have found.
Cost, and what it costs on your own machine
Cost is the work factor, from 4 to 31, and it is an exponent: each step up doubles the time. That is the whole point of bcrypt. A password hash is meant to be slow, because an attacker holding a stolen table has to pay the same price on every guess, and doubling the cost halves the number of guesses per second they get for their money.
What a given cost actually takes is a fact about the machine running it, not a number that can be written down — a phone and a server are two orders of magnitude apart. So this page measures instead of quoting: it runs one short hash on your own device when the page loads and extrapolates from there, which is why the estimate beside the cost control is blank for a moment and then appears. Above a few seconds it asks before it starts, so a mistyped digit does not read as a frozen page.
For choosing a number: 12 is what Laravel and Python's bcrypt emit by default and is a reasonable production floor today. Lower costs are for test fixtures, where a suite that seeds fifty users should not spend a second per user. Costs above about 15 are worth measuring against your real login traffic before you ship them, since every sign-in pays the price too.
# Apache: write an htpasswd line at cost 12
htpasswd -nbBC 12 alice "correct horse battery staple"
# PHP and Laravel: password_hash emits $2y$
php -r 'echo password_hash("correct horse battery staple", PASSWORD_BCRYPT);'
# Python: bcrypt emits $2b$, and gensalt takes the cost
python -c "import bcrypt; print(bcrypt.hashpw(b'correct horse battery staple', bcrypt.gensalt(12)))"The salt, and when a hash made here is not for storing
The salt is sixteen random bytes drawn fresh for every hash, and it is stored in the open inside the string. It is not a secret and was never meant to be one — its job is to make two identical passwords produce two unrelated hashes, so that a stolen table cannot be attacked one precomputed dictionary at a time.
That is also why the same password gives you a different hash every time you press the button here. It is not a fault and the old answer is not stale: a fresh salt is bcrypt working. Any of those hashes matches the password, because the salt travels inside the string that gets checked.
The advanced salt field exists for one job: reproducing somebody else's hash exactly, so that two implementations can be compared. Paste a whole hash into it and the cost and variant follow it automatically, so the right password reproduces the input character for character. Anything you produce that way is for comparing and not for storing — a salt used twice is a salt that has stopped doing its job.
A bcrypt hash cannot be reversed
People arrive at pages like this one looking for a way to decrypt a bcrypt hash. There is not one, and the reason is not that it is hard: the password is not in the string. Sixty characters carry a variant, a cost, sixteen random bytes and twenty-three bytes of output, and no arrangement of those contains a password of any length. Nothing was encrypted, so there is nothing to decrypt.
What a site can do — and what the ones advertising decryption are doing — is guess. Take a list of common passwords, hash each one against your salt at your cost, and see whether any of them matches. bcrypt is designed to make exactly that expensive, which is what the cost is for, and this tool does not offer it in any form.
The honest version of that question is usually a different one, and it has an answer: what is in this string, and does this particular password produce it? Both are on this page. Paste the hash to see everything it carries, and add a password to get a yes or a no.
Frequently asked questions
- Is my password sent to a server?
- No. Everything happens in this browser tab — the hashing runs in a Web Worker on your own machine, the password never enters the URL, and nothing is written to browser storage. Open your network panel, type a password and press the button: no request goes out. That is the difference between this page and the two sites that rank above it for the same question.
- Can a bcrypt hash be decrypted back to the password?
- No, and not because it is hard. The password is not in the string: what is there is a variant, a cost, a salt and twenty-three bytes of output, none of which contains the input. Anything offering to decrypt one is guessing common passwords against your salt, which bcrypt is specifically designed to make slow.
- Why does the same password give a different hash every time?
- Because a new random salt is drawn for each run, and the salt is part of the string. Every one of those hashes matches the same password — the checker reads the salt out of the hash you paste, so it does not need to know which run produced it.
- My password was cut at seventy-two bytes. Why was there no error?
- Because there is nothing to detect. bcrypt mixes exactly seventy-two bytes of key into its state and never reads further, so a longer password and its first seventy-two bytes produce the same digest — the truncation leaves no trace for a library to notice. This page tells you it happened, which is the most any implementation can honestly do.
- Why did my password hit the limit at thirty-six characters?
- Because the limit is seventy-two bytes rather than seventy-two characters, and Hebrew, Arabic, Russian and Greek letters cost two bytes each in UTF-8. Japanese, Korean and Chinese cost three, so the limit arrives at twenty-four characters there, and most emoji cost four. The counter under the field shows the byte count as you type.
- Which variant should I choose?
- Whichever the system you are pasting into expects: $2y$ for PHP and Laravel, $2a$ for older Spring Security, $2b$ everywhere else. It is a compatibility decision and not a security one — at any length this tool can be given, the three produce identical digests and differ only in those four characters at the front.
- What cost should I use?
- 12 is the common default and a reasonable production floor. Use a low cost for test fixtures so your suite is not paying for key stretching fifty times over, and measure anything above 15 against your real sign-in traffic first, because every successful login pays the same price an attacker does.
- Is a hash made here safe to put in a real user table?
- For seeding a database, writing a fixture or adding an htpasswd line, yes — the salt comes from your browser's cryptographic random source. But this is a scratch page rather than a credential service: if you type the salt in yourself the result stops being safe to store, which the page says at the time, and a password worth protecting is better minted where it is going to live.
- Why does my hash start with $2y$ when the code that made it says bcrypt?
- Because PHP's password_hash writes $2y$, and Laravel is built on it. It is the same algorithm as $2b$; the variant records which implementation wrote the string. A checker that refuses it is refusing the variant rather than failing to match the password.
- I pasted a hash and it says the salt was respelled. Is it broken?
- No. Twenty-two characters can encode more bits than sixteen bytes need, so four bits at the end are dropped and some salts have more than one spelling that decodes to the same bytes. Real systems have emitted those, so this page reads such a hash rather than refusing it, and tells you the canonical spelling.
Related tools
- JWT decoder / verifier
Decode and verify JSON Web Tokens — signature and claims.
- Hash generator
MD5, SHA-1, SHA-256, SHA-384 and SHA-512 at once.
- Certificate & PEM decoder
Read an X.509 certificate or CSR without OpenSSL.
- HMAC generator
Sign a message with a key, or verify a webhook signature.