All three exist because fast hash functions are wrong for passwords. Plain SHA-256 runs at billions of hashes per second on a GPU. bcrypt, Argon2, and scrypt are designed to run at hundreds or low thousands — slow enough that brute-forcing a stolen database takes years rather than hours.
They share a few things: automatic salting (so two users with the same password get different hashes), adjustable cost (so you can make them slower as hardware improves), and output that encodes the parameters used (so you can verify old hashes even after raising the cost).
Where they differ is in what "cost" means.
bcrypt
bcrypt controls cost through iteration count only. Higher cost factor means more rounds, which means more time. Simple, well-understood, and widely supported — bcrypt has been the default recommendation since 1999. That's not nothing: 25 years of production use and scrutiny is a genuine advantage.
One limitation worth knowing: bcrypt silently truncates passwords longer than 72 bytes. If a user has a very long passphrase, only the first 72 bytes are hashed. This is a known quirk, not a catastrophic flaw, but it's worth being aware of. Some libraries work around it by pre-hashing with SHA-256 before passing to bcrypt.
scrypt
scrypt adds memory as a second cost dimension. To compute a scrypt hash, you need to allocate a large block of RAM. GPUs have less memory per core than CPUs, which makes GPU-based brute-forcing more expensive relative to the hardware investment. You tune both time and memory independently.
scrypt is less widely supported than bcrypt but more supported than it used to be. It's a reasonable choice if you need more GPU resistance than bcrypt provides and Argon2 isn't available in your stack.
Argon2
Argon2 won the Password Hashing Competition in 2015. It has three variants: Argon2d (optimized against GPU attacks but potentially vulnerable to side-channel attacks), Argon2i (side-channel resistant), and Argon2id (a hybrid that's good enough at both). Use Argon2id.
It controls three things independently: time cost (iterations), memory cost, and degree of parallelism. That gives you the most flexibility in tuning for your specific server environment.
| bcrypt | scrypt | Argon2id | |
|---|---|---|---|
| Introduced | 1999 | 2009 | 2015 |
| Cost parameters | Time only | Time + memory | Time + memory + parallelism |
| Password length limit | 72 bytes | None | None |
| GPU resistance | Good | Better | Best |
| OWASP rank (2024) | 2nd | 3rd | 1st |
| Library availability | Every language | Most languages | Growing |
What OWASP recommends in 2026
Argon2id first, bcrypt as the fallback, scrypt as a third option. This has been stable for a few years. OWASP's suggested Argon2id parameters are 64 MB of memory, 3 iterations, and 4 threads of parallelism, with at least a 16-byte salt and 32-byte output. For bcrypt, a minimum cost factor of 10 — they prefer 12.
What to actually use
Starting a new project: try Argon2id first. In Python it's argon2-cffi. In Node.js there's argon2 or @node-rs/argon2. In PHP 7.2+, password_hash() with PASSWORD_ARGON2ID is built in. If Argon2 isn't available or the library situation looks rough in your ecosystem, bcrypt is a perfectly acceptable choice.
Working in an existing codebase: you'll almost certainly find bcrypt, because it's been around forever and has solid libraries everywhere. bcrypt at cost 10 or higher is still fine. If you find unsalted MD5 or SHA-1 being used for passwords, that's the actual problem to fix first.
The bcrypt tool here runs entirely in the browser — try different cost factors and see how long they take on your machine.