Bcrypt Hash Generator_

bcrypt is the one function on this site that is trying to be slow. Press the button and notice the pause — that pause is the whole design, because an attacker with a stolen database pays it too, once per guess, and the cost factor is an exponent rather than a multiplier.

The salt comes back inside the output, which is why a bcrypt hash is sixty characters rather than a bare digest and why there is no second column to store. Everything needed to check a password later is in that one string.

toolkit.codes/bcrypt-generator
Hash · 60 characters, salt included

UTF-8
Ready
100% LOCAL
Input
A password, and a cost factor from 4 to 16. The salt is generated for you from the browser's cryptographic random source — a salt you choose yourself is a salt somebody else can predict.
Output
The full modular-crypt string: variant, cost, 22-character salt and 31-character digest, sixty characters in total. That whole string is what you store.
Processing
Blowfish, in this tab. The cipher's constants are the hexadecimal digits of pi, derived here rather than transcribed, and the implementation is checked against the canonical OpenBSD test vectors.
Limits
bcrypt reads at most 72 bytes of a password and silently ignores the rest. That is in the original design, it is measured in bytes rather than characters, and the page warns when your input crosses it.
Why slow is the feature
Every other hash here is judged on throughput. bcrypt is judged on cost: the factor is an exponent, so 12 runs the key schedule 4,096 times and 13 runs it 8,192. Raising it by one doubles what an offline attacker spends per candidate and adds a few hundred milliseconds to a login nobody performs in a loop. That asymmetry is the entire product, and it is why the number is stored in the hash — so you can raise it later and rehash people as they sign in.

A cost factor is a decision, not a default

What the number actually buys

The cost is an exponent: the expensive part of the key schedule runs 2^cost times. Going from 10 to 12 is not twenty per cent more work, it is four times more. For your users that is the difference between an imperceptible login and a slightly less imperceptible one; for somebody working through a leaked table offline it multiplies the whole campaign. Pick the highest number your slowest server can absorb at your busiest minute, and write down why you picked it.

The salt is in the output, and that is deliberate

A bcrypt hash carries its own parameters. $2b$ names the variant, 12 is the cost, the next twenty-two characters are the salt and the last thirty-one are the digest. There is no separate salt column to design, no risk of the two rows drifting apart, and no way to verify a password without the exact settings that produced it — which is precisely why they travel together. It also means a hash from 2015 at cost 10 still verifies today, and tells you it should be upgraded.

Seventy-two bytes, and then nothing

bcrypt reads the first seventy-two bytes of a password and discards the remainder without complaint. A passphrase longer than that is truncated, so two different long passphrases sharing an opening can hash identically — and because the limit is in bytes, an emoji costs four and an accented letter two, so a "short" password can reach it sooner than expected. The usual fix is to hash the password with SHA-256 first and feed the digest to bcrypt, which keeps the length fixed. Doing that has its own footgun: base64 the digest rather than passing raw bytes, or a null byte inside it will truncate the input all over again.

The variant letters, and the bug behind them

$2a$ was the original. In 2011 a bug was found in a widely used C implementation that mishandled bytes above 127, so $2x$ and $2y$ were introduced to mark hashes made with the broken and the fixed behaviour, and OpenBSD chose $2b$ for its own correction. In practice modern libraries emit $2b$ and verify all of them, and the letters matter only when you are migrating old rows that contain non-ASCII passwords.

When to reach for Argon2 instead

bcrypt is expensive in time and cheap in memory, which is exactly the shape a graphics card likes. Argon2id costs memory as well, and memory is the resource that does not parallelise cheaply — which is why it won the Password Hashing Competition and why new systems are usually pointed at it. bcrypt remains a perfectly defensible choice: it is twenty-five years old, has no practical break, and is available everywhere. The wrong answer is neither of them; it is a fast hash with a salt bolted on.

Set the cost, hash, and watch the clock

  1. 01Type a password and choose a cost. Twelve is the common recommendation for a new system; four exists for tests and protects nothing.
  2. 02Press Hash it. The elapsed time appears underneath — raise the cost by one and watch it roughly double, which is the property the whole function is built on.
  3. 03Read the breakdown to see where the variant, cost, salt and digest sit inside those sixty characters.
  4. 04To check an existing hash, paste it into the last field. The password above is tested against it using the cost and salt the hash already carries.

Seeding a development database

Fixtures need a real hash that the application will accept. Generating one here avoids running the app just to create a test account.

What you want the login to be
password: hunter2
cost: 12
What goes in the fixture
$2b$12$……………………………………………………………………………………………
(60 characters — one column, salt included)

Deciding what cost your server can carry

The right factor depends on your hardware, not on a blog post. Timing it here gives a first estimate before you benchmark on the real machine.

Measured in the browser
cost 10 → ~60 ms
cost 12 → ~250 ms
cost 14 → ~1 s
What that means
A server is faster than a browser tab.
Benchmark there, then pick the highest
your login endpoint can absorb.

A long passphrase that is quietly cut short

Everything past seventy-two bytes is discarded. Two passphrases sharing a long opening produce the same hash, and nothing warns you.

Two different passphrases
"correct horse battery staple …" + 50 more chars
"correct horse battery staple …" + different tail
Same hash
Identical, because only the first 72
bytes were ever read.

Upgrading the cost of existing accounts

The cost lives in the hash, so old rows keep working. Rehash on successful login, when you have the plaintext for a moment.

Stored in 2018
$2a$10$…
On the next successful sign-in
verify with cost 10 → true
rehash at cost 12 → store the new string

Choosing a cost factor

CostKey-schedule roundsWhat it is for
416The minimum the format allows. Tests only — it offers no meaningful resistance.
8256A sensible default around 2005. Too fast for anything holding real accounts today.
101,024The default in several libraries, and the floor most guidance now gives.
124,096The common recommendation for new systems — roughly a quarter-second per hash on a server.
1416,384Noticeably slow. Reasonable for high-value accounts where logins are infrequent.
1665,536The maximum. Several seconds per attempt, which is a denial-of-service surface as well as a defence.

The rounds column is asserted in the test suite to equal two to the power of the cost, because a table that drifts from the arithmetic it describes is worse than no table.

Getting bcrypt right

  • Store the whole sixty-character string in one column. It already contains the variant, the cost and the salt, and splitting it apart invents a synchronisation problem that did not exist.
  • Rehash on successful login when you raise the cost. It is the one moment you hold the plaintext, and it upgrades accounts gradually without a migration.
  • Never trim or normalise a password before hashing beyond what you will do identically at verification time. A stray trim on one side and not the other locks people out.
  • Benchmark on the hardware that will run it. A browser tab and a production server differ by several times, and the right cost is a property of the machine.
  • Rate-limit the login endpoint anyway. A deliberately slow hash is a defence against offline attacks on a stolen database, not against somebody guessing through your front door.

Where bcrypt is used wrongly

The 72-byte cut is silent

No error, no warning, no truncation notice from any library. Long passphrases and passwords containing multi-byte characters reach it sooner than people expect, and two that share an opening will verify against each other.

Pre-hashing with raw bytes reintroduces truncation

Feeding a raw SHA-256 digest to bcrypt to dodge the length limit works until the digest contains a null byte, which cuts the input short. Base64 the digest first.

A cost chosen once and never revisited

Hardware gets faster and the number does not. A factor that was generous in 2015 is thin now, and because the cost lives in each hash the fix is gradual rather than a rewrite.

Hashing on the client and treating it as a password

If the browser sends a hash and the server compares it directly, that hash is the password. bcrypt belongs on the server side of the boundary.

Algorithm, constants and verification

Construction
The EksBlowfish key schedule: Blowfish set up with both password and salt, then re-keyed 2^cost times, followed by 64 encryptions of the string "OrpheanBeholderScryDoubt".
Constants
Blowfish's 18-word P-array and four 256-word S-boxes are the first 8,336 hexadecimal digits of pi. They are computed here with Machin's formula and BigInt rather than pasted in, and the test suite checks the published first and last words against the derivation.
Verified against
The canonical OpenBSD and jBCrypt vectors — the same five inputs every implementation is checked with, including the empty password and one full of punctuation.
Output
Modular crypt format: $2b$, two cost digits, a 22-character salt and a 31-character digest, using bcrypt's own base64 alphabet, which is not the RFC 4648 one.
Salt
128 bits from crypto.getRandomValues. Generated per hash, never reused, and never derived from the password.
Network
None from tool code. A test sweep calls every function this page uses with fetch and XMLHttpRequest replaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.

Questions about bcrypt

What cost factor should I use?

Twelve is the usual recommendation for a new system in 2026, and ten is the floor most guidance gives. But the number is a property of your hardware rather than of bcrypt: time a hash on the machine that will actually run it, then take the highest value your login endpoint can absorb at peak traffic. Then write down the reasoning, because the right number moves.

Why is bcrypt slow?

Because speed helps the attacker and nobody else. A user logs in occasionally; somebody working through a stolen database runs the function billions of times, so making each call cost real work is the only lever that helps you and hurts them. The cost factor is an exponent, so each step up doubles their bill.

Do I need to store the salt separately?

No, and you should not. The salt is inside the sixty-character hash along with the variant and cost. Store the whole string in one column — splitting it apart creates a way for the two halves to drift and buys nothing.

Why does bcrypt ignore part of my password?

It reads at most seventy-two bytes and discards the rest, silently, by design. The limit is in bytes rather than characters, so accented letters and emoji reach it sooner. If you need to accept long passphrases, hash them with SHA-256 first and base64 the digest before passing it to bcrypt.

What is the difference between $2a$, $2b$ and $2y$?

They mark which implementation behaviour produced the hash. A 2011 bug in a widely used C version mishandled bytes above 127; $2x$ and $2y$ were introduced to distinguish broken from fixed, and OpenBSD used $2b$ for its own correction. Modern libraries write $2b$ and verify all of them, and the letter only matters when migrating old non-ASCII passwords.

Is bcrypt still good enough, or should I use Argon2?

Both are defensible. bcrypt costs time and very little memory, which suits graphics cards; Argon2id costs memory as well, which does not parallelise cheaply, and it won the Password Hashing Competition for that reason. New systems are usually pointed at Argon2id. Migrating a working bcrypt deployment purely on principle is rarely the best use of the effort.

Can a bcrypt hash be reversed?

No more than any other one-way function, and considerably less usefully: the salt means precomputed tables do not apply, and the cost factor means brute force is expensive per attempt. That combination is the point — it is not that reversal is harder in principle, it is that guessing is priced.

Why does the same password give a different hash each time?

A fresh random salt goes into every one. Two accounts sharing a password therefore end up with unrelated strings, so cracking one tells an attacker nothing about the other and no single precomputed table covers both. Checking still works, because the salt travels inside the hash and is read back out when verifying.

Is the password I type here sent anywhere?

No. The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on.