bcrypt vs Argon2 vs scrypt vs PBKDF2: A 2026 Decision Framework
Most "use bcrypt" posts are from 2014. Argon2 won the Password Hashing Competition in 2015 and nobody updated. Here is the actual 2026 decision framework for picking a password hashing algorithm.

The canonical Stack Overflow answers and the canonical security blog posts on "which password hashing function should I use" are nearly all from 2012 to 2014, recommending bcrypt. Argon2 won the Password Hashing Competition in 2015. A decade later, half the production systems I audit are still on bcrypt with cost factors picked in the Obama administration. Here is the actual 2026 decision framework.
This is not a "bcrypt is bad" post. bcrypt remains safe in 2026 if it's tuned correctly. It is a "the conversation moved on and you may have missed it" post. The four algorithms have specific shapes, and one of them is the right pick for your situation. For the full algorithm-by-algorithm breakdown, see the complete guide to password hashing. Let's get to it.
The four algorithms in one paragraph each
PBKDF2 (2000). RFC 2898, designed by RSA Labs. Takes a base hash (typically SHA-256 or SHA-512), salts the password, and iterates the hash N times. The only one of the four that is FIPS (Federal Information Processing Standards, the U.S. government's cryptographic-module certification program) 140-2 and FIPS 140-3 approved, per NIST SP 800-132. That is why it survives in regulated environments long after newer algorithms appeared. Weakness: easily GPU-accelerated, because each iteration is a cheap SHA call. Modern attackers running ASIC (application-specific integrated circuit) or GPU rigs can test billions of PBKDF2 candidates per second. SHA-256 is the underlying primitive.
bcrypt (1999). Designed by Niels Provos and David Mazières, based on the Blowfish cipher, in a 1999 USENIX paper. Adaptive cost factor (the famous "work factor"), 72-byte password ceiling (a real gotcha), 184-bit output. The brilliant property is that the Blowfish key schedule is intentionally cache-unfriendly, which historically made it expensive to parallelise on GPUs. That property degraded as GPUs got better at irregular memory access, but bcrypt remains slower to attack than PBKDF2. bcrypt reference on Hash Lab.
scrypt (2009). Designed by Colin Percival in a 2009 paper, the first "memory-hard" password hash to see real adoption. Tunes both CPU cost and memory cost, with the explicit goal of making custom hardware attacks expensive (because RAM is more expensive than gates). Used heavily in cryptocurrency mining-resistance for a while. scrypt reference.
Argon2 (2015). Winner of the Password Hashing Competition, designed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich. Three variants: Argon2d (data-dependent, fastest, vulnerable to side-channel attacks), Argon2i (data-independent, slower, side-channel resistant), Argon2id (hybrid, the recommended default). Tunes time cost, memory cost, and parallelism independently. Standardised as RFC 9106 in 2021. Argon2id reference.
The decision framework
Four buckets. Pick the one that matches your situation.
Bucket 1: greenfield code in 2026 - use Argon2id
You are writing new code, no library constraints, no FIPS requirement, no need to integrate with existing infrastructure. Use Argon2id.
Recommended parameters as of 2026 (server-side, interactive login, single password verification): the OWASP Password Storage Cheat Sheet lists five parameter sets it treats as equally strong, trading CPU time for memory. The baseline is time cost t=2, memory cost m=19,456 KiB (19 MiB), parallelism p=1, which lands around 100ms of verification time on a modern x86 server core, a reasonable tradeoff between user experience and attacker cost.
If you have memory to spare and would rather spend less CPU time (servers with 32+ GB RAM and predictable login load), OWASP's higher-memory option is m=47,104 KiB (46 MiB), t=1, p=1. Memory cost is what crushes ASIC and GPU attackers more than time cost does, so lean toward the higher-memory end of the range if you have the headroom.
Library: argon2-cffi for Python, argon2 crate for Rust, @node-rs/argon2 for Node (avoid the pure-JS implementations, they are 100x slower and you will pick weaker parameters to compensate). Hash Lab's password hashing tool lets you tune parameters interactively and see the resulting verification time on your hardware.
Bucket 2: constrained environment or library-stuck - bcrypt is still safe
You are on a stack where the de-facto password hashing library is bcrypt (Rails, older Spring, anything that picked bcrypt in 2014 and never moved). Switching to Argon2id is correct in principle but expensive in practice if there is no business reason.
If you stay on bcrypt, recommended cost factor as of 2026: cost=12 minimum, cost=13 if your server can absorb it. Verify the resulting login time stays under 250ms on your production hardware. Anything below cost=10 is no longer safe in 2026 because consumer-grade GPUs can test it at hundreds of thousands of guesses per second.
Two bcrypt gotchas that bite people:
- The 72-byte password ceiling. bcrypt truncates passwords at 72 bytes. If your application allows longer passwords, you must pre-hash with SHA-256 or SHA-512 before passing to bcrypt. The pre-hash should be deterministic and unsalted (you are not trying to add security, you are trying to compress).
- Null byte truncation in some implementations. A handful of older bcrypt libraries truncate at the first null byte in the pre-hashed input, which means a base64-encoded SHA-256 pre-hash (which never has nulls) is safer than a raw binary pre-hash.
Bucket 3: FIPS-required environment - PBKDF2 with SHA-256
You are working in an environment that requires FIPS 140-2 or FIPS 140-3 approved primitives. Government, certain healthcare, certain financial. Argon2 is not FIPS-approved (as of mid-2026; the IETF process is ongoing). bcrypt is not FIPS-approved. scrypt is not FIPS-approved.
PBKDF2 with SHA-256 or SHA-512 is your only sanctioned option.
Recommended iteration counts as of 2026, per the OWASP Password Storage Cheat Sheet and NIST SP 800-132: SHA-256: 600,000 iterations minimum. SHA-512: 220,000 iterations minimum. These are dramatically higher than the 10,000-iteration values that float around in legacy code. OWASP has raised its recommended PBKDF2 iteration counts more than once over the past decade as GPU compute has gotten cheaper, so check the current cheat sheet rather than trusting a number from an old blog post.
You can layer a memory-hard wrapper around PBKDF2 (the "hash everything twice" trick using PBKDF2 followed by a non-FIPS memory-hard function) if you really need both FIPS compliance and memory-hardness. This is an advanced pattern, so consult your auditor before deploying it.
Bucket 4: scrypt
scrypt is rarely the right pick in 2026. Argon2id does everything scrypt does, better (because Argon2 had 6 more years of cryptanalysis baked into the design) and is now well-supported across mainstream stacks. The one place scrypt still wins is when you are integrating with an existing system that already uses scrypt (Litecoin-derivative codebases, certain hardware wallets), and you don't want to introduce a new primitive.
If you must use scrypt, OWASP's recommended parameters are N=2^17, r=8, p=1, which consumes 128 MiB of memory per verification. If memory budget is tight, the next tier down is N=2^16, r=8, p=2 (64 MiB), which OWASP treats as an equally strong alternative that trades memory for parallelism.
Cost-parameter tuning, generally
The right cost parameter is the highest one your servers can absorb without degrading the login user experience. The standard tradeoff: target 100 to 250ms of verification time on a production server core. Below 100ms, you are leaving attacker-cost on the table. Above 250ms, real users will feel the lag and complain.
The trap people fall into: they pick a parameter, ship it, and never revisit. Hardware gets 10x faster every 5 to 7 years. Your cost parameter should be revisited on the same cadence as your dependency upgrade cycle. Set a reminder.
One technique that helps: store the cost parameter inside the hash record (which is the default for bcrypt, Argon2, and scrypt; PBKDF2 requires you to track it yourself). On verify, if the stored cost is below your current target, transparently rehash the password at the new cost during the user's successful login. After 6 to 12 months you will have rotated most active users without a forced password reset.
Migration patterns: how to rehash gracefully
You inherited a legacy system on PBKDF2 with 10,000 iterations, or on bcrypt cost=8, or on something embarrassing like unsalted MD5. The good news is you don't need a forced password reset.
The pattern is to wrap the old hash in the new one at rest, and unwrap on verify. Concretely: take every existing legacy_hash and compute new_hash = argon2id(legacy_hash). Store new_hash. Mark the user record as "legacy wrapped." On login, compute legacy_hash from the user's submitted password, then verify new_hash matches argon2id(legacy_hash). The user's password never touches the old algorithm during verification because the legacy_hash is already in your database.
This pattern protects every password in the database immediately (because the at-rest values are now Argon2id-wrapped) and lets you migrate users individually at next login. After you've rotated everyone who logs in within 12 months, you can force a password reset on the long tail. Best practices for user authentication covers the broader security framework around this.
The traps that catch people
Using SHA-256 alone (no key stretching). If your codebase computes sha256(password) or even sha256(salt + password) and stores that, you have a vulnerable system. SHA-256 is a fast hash; it is designed to be fast. A modern GPU rig tests hundreds of billions of SHA-256 candidates per second. Use one of the four password-specific algorithms above. The Hash Lab identifier can tell you what algorithm produced a given hash string if you're inheriting a system and aren't sure.
Storing the salt separately from the hash. Modern bcrypt, scrypt, and Argon2 hash strings include the salt and parameters in the encoded output. There is no reason to store them in a separate column, and doing so introduces a class of bugs where you accidentally lose the salt during a migration. Trust the algorithm's serialisation format.
Inadequate cost parameters. Half the bcrypt deployments I audit run at cost=10, which was a reasonable default in 2014 and is not in 2026. The same applies to PBKDF2 systems still running 100,000 iterations.
Per-user pepper without a key management plan. A pepper (a secret added to every password before hashing, not stored in the database) is a real defense-in-depth measure, but only if you actually have a key management story for it. Without rotation, hardware backing, and recovery procedures, the pepper just becomes another secret that leaks with the database.
Mixing client-side and server-side hashing without understanding what you're protecting against. Client-side hashing protects the password in transit (mostly handled by TLS anyway) but does not protect against database compromise (because the client-hashed value becomes the new effective password). Server-side hashing protects against database compromise. You almost always want server-side. If you want both, do client-side as a transport encoding and server-side as the durable storage hash; do not skip the server-side.
Frequently Asked Questions
Which password hashing algorithm should I use for a new project in 2026?
Argon2id, unless you have a library constraint or a FIPS requirement. OWASP's baseline parameters for a server-side interactive login are time cost t=2, memory cost m=19,456 KiB (19 MiB), and parallelism p=1, which lands around 100ms of verification time on a modern server core. If you have more memory budget to spend, m=47,104 KiB (46 MiB) with t=1 is an equally strong alternative that uses less CPU time.
Is bcrypt still safe to use in 2026?
Yes, if it is tuned correctly. Use a cost factor of at least 12, or 13 if your servers can absorb the extra time, and confirm login time stays under 250ms in production. Anything below cost=10 is no longer safe, since consumer GPUs can test it at hundreds of thousands of guesses per second.
How do I work around the bcrypt 72-byte password limit?
bcrypt truncates passwords at 72 bytes. If your application allows longer passwords, pre-hash the password with SHA-256 or SHA-512 before passing it to bcrypt. The pre-hash should be deterministic and unsalted, since its job is compression, not added security. A base64-encoded pre-hash also avoids the null-byte truncation bug in some older bcrypt implementations.
What should I use in a FIPS-required environment?
PBKDF2 with SHA-256 or SHA-512 is the only sanctioned option, since Argon2, bcrypt, and scrypt are not FIPS-approved as of mid-2026. Use at least 600,000 iterations with SHA-256 or 220,000 iterations with SHA-512, well above the 10,000-iteration values that still float around in legacy code.
Do I need to force a password reset when migrating to a new hashing algorithm?
No. Wrap the existing legacy_hash inside the new algorithm, for example new_hash = argon2id(legacy_hash), and mark the record as legacy wrapped. On login, recompute legacy_hash and verify it against new_hash. This protects every password at rest immediately and migrates each user individually the next time they log in, so you can leave a forced reset for only the long tail of users who have not returned after 12 months.
When does scrypt make more sense than Argon2id?
Almost never for new code. Argon2id does everything scrypt does, and does it better, since it benefited from six more years of cryptanalysis. The one exception is integrating with an existing system that already uses scrypt, such as certain Litecoin-derivative codebases or hardware wallets, where introducing a new primitive is not worth the churn.
The broader context
Two adjacent points worth making explicitly.
First, passwords are not dead, despite a decade of optimism. The right long-term move is to layer passwords with passkeys, WebAuthn, and other phishing-resistant factors. But the password column is not going away in 2026, which means picking a current password hashing algorithm and tuning it correctly still matters.
Second, AI-generated passwords are not a substitute for proper hashing, but they are part of the bridge. AI password generators covers the user-side bridge.
If you want to experiment with the four algorithms hands-on (input a password, see the output, tune parameters, observe the verification time on your hardware), Hash Lab's password hashing tool runs everything in-browser via WebCrypto and WASM (WebAssembly). The full algorithm catalog (including hashing primitives like SHA-256, BLAKE3, and the message authentication codes) is at Hash Lab.
The summary, in one sentence: greenfield is Argon2id, legacy bcrypt is still fine if you raise the cost, FIPS environments use PBKDF2, scrypt is rarely the right pick anymore. Pick the bucket, set the parameters, and ship something that's not from 2014.
More from Deepak Gupta
Every page on guptadeepak.com is hand-curated by Deepak Gupta. Pick a thread:
- About Deepak Gupta
Founder, cybersecurity architect, and writer at guptadeepak.com.
- My journey
From LoginRadius (2013, 1B+ users) to GrackerAI, in milestones.
- Publications & patents
Books, free e-books, a journal special issue, and five granted patents.
- Research Hub
Curated research, buyer's guides, vendor comparisons, and technical deep-dives.
Get the newsletter
New writing on identity, AI security, and building software, delivered when it ships. No tracking pixels, no funnels, unsubscribe with one click.