← Home

bcrypt Stops Reading at 72 Bytes and Does Not Tell You

Two passwords that look nothing alike can be the same password. The cut is measured in bytes, not characters, so it lands mid-character — and 24 Chinese characters is already the whole budget.

A band of pale blue blocks running left to right, one oversized block cut cleanly in half, and everything past the cut crumbling into fragments.

A user sets a long passphrase. Months later they mistype the tail of it and get in anyway. Nothing is broken, no one filed a bug, and the password they think protects their account is shorter than the one they chose.

bcrypt reads 72 bytes and throws the rest away

Measured on Node v25.9.0 with bcrypt 6.0.0 and bcryptjs 3.0.3: both accept a password of any length, hash the first 72 bytes, and discard everything after it. No exception, no warning, no return value that hints anything happened.

The boundary is exact. Take a 71-byte password, add one character, and it is a different password. Take a 72-byte one, add one character, and it is the same password.

That second sentence is the whole article.

Two different passwords, one hash

Below are two inputs. The defaults share their first 72 bytes and differ after that. Nothing is hashed, sent or stored — the counting happens in your browser.

What bcrypt actually reads

Do not type a password you actually use. Nothing here is hashed, sent or stored — the counting happens in your browser — but a password you have typed into a web page is a password you should change.

bytes 79 · characters 79 · over 72 bytes — the rest is discarded

correct-horse-battery-staple-correct-horse-battery-staple-correct-horse-battery

bytes 79 · characters 79 · over 72 bytes — the rest is discarded

correct-horse-battery-staple-correct-horse-battery-staple-correct-horse-staple!

To bcrypt, are these the same password?

Same password. Register with one, sign in with the other.

How many characters fill 72 bytes

  • a 72 characters
  • é 36 characters
  • 密 24 characters
  • 🔐 18 characters

The limit is 72 bytes. Behaviour measured on Node v25.9.0 with bcrypt 6.0.0 on 2026-08-07.

The verdict line is not a simulation of bcrypt. It compares the first 72 bytes, because that is the entire thing bcrypt does with the input. When it says same password, it means you could register with one and sign in with the other.

The limit is bytes, and text is not bytes

bcrypt is built on the Blowfish key schedule, which consumes raw bytes. Your password only becomes bytes after it is encoded, and UTF-8 does not charge every character the same:

bytes each characters that fit
ASCII 1 72
Accented Latin (é) 2 36
Common Chinese (密) 3 24
Emoji (🔐) 4 18

An English speaker has to work at hitting 72. A Chinese speaker gets there with a perfectly ordinary passphrase — twenty-four characters is a sentence, not an endurance test. The same limit is a curiosity in one language and a live problem in another, which is exactly the kind of bug that survives review by an English-speaking team.

The cut does not respect characters

This is the part that surprised me enough to check twice.

密 encodes as e5 af 86. 寇 encodes as e5 af 87. They share their first two bytes. Put either at the end of a password where the cut falls after byte 72 and bcrypt keeps e5 af from both:

"a" × 70 + 密   →  73 bytes
"a" × 70 + 寇   →  73 bytes
first 72 bytes  →  identical

Hashing the first and verifying the second returns true. As a control, 天 (e5 a4 a9) differs at the second byte and correctly fails.

So two passwords that share no visible ending, that a human would never call the same, are the same credential. The truncated tail is not even valid UTF-8 — it is two thirds of a character.

One piece of folklore that is wrong here

You will read that bcrypt stops at a NUL byte, the way a C string does. On bcrypt 6.0.0 it does not: hashing "secret" + NUL + "ignored-tail" and verifying "secret" against it returns false.

I include this because it is the same shape of claim as everything above — a byte-level behaviour repeated confidently in a hundred comment threads — and it happens to be false for the library most Node projects actually install. Measure the one you have.

What to do

The limit is not the bug. The silence is.

  1. Reject over-length passwords at registration and say so. A user who is told their passphrase is too long picks another one. A user who is silently truncated believes in a password that does not exist.
  2. Count bytes, not characters. password.length is the wrong number in every language that needs more than one byte per character — and it is wrong in the direction that lets bad input through.
  3. If your users write Chinese, Japanese or Korean, your real ceiling is 24 characters. Not a theoretical limit worth a footnote: a length people type.

If you want a password field with no length ceiling at all, that is an argument for a different KDF, not for a longer bcrypt. But most systems do not need one — they need to stop lying about the password they stored.

Common follow-ups

Does bcrypt throw an error on a long password?

Not in Node. Measured on bcrypt 6.0.0 and bcryptjs 3.0.3, both accept a password of any length, hash the first 72 bytes and discard the rest without raising anything. Some implementations in other languages do reject long input, so this is a per-library behaviour you have to check rather than assume.

Why 72 bytes and not 72 characters?

The limit comes from the Blowfish key schedule bcrypt is built on, which consumes raw bytes. Text only becomes bytes after encoding, and UTF-8 gives one byte to ASCII, two to most accented Latin letters, three to common Chinese characters and four to emoji. The limit is fixed; how much text fits under it is not.

What happens if the cut falls inside a character?

bcrypt keeps whatever bytes fit and drops the remainder of that character. Any other character whose encoding starts with the same surviving bytes then produces an identical password — 密 and 寇 both begin e5 af, so they collide when the cut lands after their second byte.

Does a NUL byte end the password?

Not in the version measured here. That claim comes from implementations built on C string handling, and it is worth testing rather than assuming: on bcrypt 6.0.0, a password containing a NUL byte does not verify against the text before it.

So what should I actually do?

Reject over-length passwords at registration and say so, instead of truncating in silence. Measure the length in bytes rather than with a character count, and if your users write in a language where a character costs three bytes, remember that the practical ceiling is 24 characters, not 72.