Security2026-04-19

bcrypt vs Argon2: Which Password Hash Should You Use in 2026?

bcrypt has been the safe default for 25 years. Argon2 won the Password Hashing Competition. Here's how to choose between them — and why both are fine.

bcryptargon2password-hashingsecuritycryptographyscrypt

bcrypt vs Argon2: Which Password Hash Should You Use in 2026?

Every serious auth tutorial eventually tells you: "Don't use SHA-256 for passwords. Use bcrypt or Argon2." That's correct, but it's also where most tutorials stop. If both are acceptable, which one do you pick? And why?

The short answer: both are fine for production. Argon2 is the theoretical winner. bcrypt is the pragmatic winner. The practical differences matter less than getting the cost parameters right.

Why Not Just Use SHA-256?

Before comparing the two, it's worth remembering what problem we're solving. A password hash needs to be slow. Fast cryptographic hashes like SHA-256 are designed to be as efficient as possible — the exact opposite of what you want for passwords.

A modern GPU can compute billions of SHA-256 hashes per second. Given a leaked database of SHA-256 password hashes and a consumer GPU, an attacker will crack every common password (anything under ~10 characters) within hours.

Password hashing functions like bcrypt and Argon2 deliberately burn resources — CPU time, memory, or both — to slow the attacker down to a few thousand guesses per second per core, rather than billions.

You can see the speed difference for yourself: generating a SHA-256 hash with the SHA-256 Generator is instant; generating the same password through the bcrypt Generator takes a perceptible fraction of a second. That delay is the whole point.

What bcrypt Is

bcrypt was introduced at USENIX 1999 by Niels Provos and David Mazières. It's based on the Blowfish cipher, configured to be deliberately expensive to compute.

Its defining characteristic is the cost factor — a small integer (typically 10, 12, or 14) that exponentially controls how much CPU work is required:

  • cost = 10 → 2^10 = 1,024 rounds of the expensive key schedule
  • cost = 12 → 2^12 = 4,096 rounds (4× slower)
  • cost = 14 → 2^14 = 16,384 rounds (16× slower)

A bcrypt hash looks like this:

$2b$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
 │   │  │                      │
 │   │  │                      └─ hash (31 chars, Base64-like)
 │   │  └─ 22-char Base64-encoded salt (128 bits)
 │   └─ cost factor (12)
 └─ algorithm version (2b)

The salt is embedded in the hash output, so you never need to store it separately. Verification is a single function call that re-derives the hash from the supplied password and compares.

What Argon2 Is

Argon2 won the Password Hashing Competition in 2015 — a multi-year, open contest specifically designed to find a successor to bcrypt and scrypt. It was published as RFC 9106 in 2021.

Argon2 comes in three variants:

  • Argon2d — maximizes resistance to GPU cracking, but is vulnerable to side-channel attacks. Use for cryptocurrencies and offline contexts.
  • Argon2i — resistant to side-channel attacks, weaker against GPU cracking. Use when the hash is computed in an untrusted environment.
  • Argon2id — a hybrid of the two. This is what you want for password hashing. It's the default recommendation in RFC 9106.

Argon2 takes three tunable parameters instead of one:

Parameter Symbol What it controls
Memory m KB of RAM used per hash
Iterations t Number of passes over memory
Parallelism p Number of parallel threads

OWASP's 2024 recommendation for Argon2id is m = 19456 (19 MiB), t = 2, p = 1.

An Argon2 hash looks like:

$argon2id$v=19$m=19456,t=2,p=1$c2FsdA$hash...

The Core Difference: Memory-Hardness

The real reason Argon2 exists is memory-hardness.

bcrypt uses a fixed ~4 KB of memory regardless of cost factor. That means a determined attacker can build ASIC or FPGA hardware that parallelizes bcrypt cracking at massive scale — thousands of bcrypt cores on a single chip, each with its own small memory.

Argon2 can be configured to require hundreds of megabytes of memory per hash. An ASIC that parallelizes Argon2 must physically include all that RAM, which drastically increases cost per core. This is called memory-hard and it's the single biggest argument for Argon2 over bcrypt.

In practice, for server-side web authentication, both algorithms force attackers to use commodity hardware (CPUs or GPUs). The gap between bcrypt and Argon2 in real-world attacker economics is smaller than the gap between bcrypt and SHA-256 — which is enormous.

Comparison at a Glance

bcrypt Argon2id
Year 1999 2015
Based on Blowfish BLAKE2b + custom mixing
Memory-hard? No (~4 KB fixed) Yes (configurable)
Tunable parameters 1 (cost) 3 (memory, time, parallelism)
Maximum password length 72 bytes (quietly truncates) Effectively unlimited
Salt Built into hash output Built into hash output
Output length 60 chars (fixed) Variable, configurable
Library support Universal Growing — mainstream in modern languages
Standardization De facto RFC 9106
Best against GPU cracking Good Better
Best against ASIC cracking Weak Strong

The 72-Byte Trap

bcrypt's most famous footgun: it silently truncates passwords at 72 bytes. If a user's password is 80 characters long, the last 8 characters are ignored. Two different long passwords that share the first 72 bytes will produce the same hash.

In practice, 72 bytes is long enough that no sane user is affected — but password managers generating 100-character random passwords can be. Fix: either enforce a max length in your UI (most sites do) or pre-hash with SHA-256 before bcrypt:

pre_hash = hashlib.sha256(password.encode()).digest()  # 32 bytes, always
bcrypt_hash = bcrypt.hashpw(pre_hash, bcrypt.gensalt())

Some libraries (notably Passlib in Python) do this automatically; others don't. Test before you trust.

Argon2 has no equivalent length limit.

Cost Parameter Selection

Whatever algorithm you pick, cost parameters matter more than the algorithm choice. A properly tuned bcrypt beats a badly configured Argon2 every time.

The rule is: pick the highest cost that still hashes in ~250 ms on your server. Users will not notice a quarter second at login; attackers feel every millisecond.

Benchmark on your actual production hardware, not your laptop. As CPUs get faster, revisit every 1–2 years.

bcrypt

import bcrypt, time
for cost in range(10, 16):
    start = time.time()
    bcrypt.hashpw(b"test password", bcrypt.gensalt(cost))
    print(f"cost={cost}: {(time.time() - start) * 1000:.0f}ms")

Look for the highest cost that stays under ~250 ms. As of 2026, that's typically cost=12 on average web servers.

Argon2id

from argon2 import PasswordHasher
for m in [15360, 19456, 47104]:
    ph = PasswordHasher(memory_cost=m, time_cost=2, parallelism=1)
    # benchmark...

OWASP's baseline of m=19456, t=2, p=1 is a reasonable starting point.

Rehashing on Login

Cost parameters get more aggressive over time. To migrate old hashes to newer parameters without forcing a mass password reset:

  1. User logs in; you verify their password against the stored hash.
  2. If the password is correct and the stored hash uses outdated parameters, re-hash with the new parameters and update the database.
  3. If the password is wrong, do nothing — the attacker can't get the new hash without knowing the password.

Most mature libraries expose a needs_rehash() helper for exactly this pattern. Use it.

What About scrypt?

scrypt was Colin Percival's 2009 answer to bcrypt's lack of memory-hardness. It predates Argon2 and influenced its design.

scrypt is still cryptographically sound — it's used in Litecoin, LUKS, and OpenBSD. But for new password-hashing code in 2026, Argon2id is the preferred successor. scrypt's parameter naming (N, r, p) is notoriously confusing, and library support is narrower than either bcrypt or Argon2.

Which Should You Actually Use?

Use Argon2id if:

  • You're starting a new project with no existing auth.
  • Your stack has a well-maintained Argon2 library.
  • You want to follow current NIST / OWASP / IETF guidance.

Use bcrypt if:

  • You already have bcrypt hashes in your database (don't migrate for migration's sake).
  • Your language or framework has poor Argon2 support.
  • You need maximum compatibility (e.g., writing a library others will use).
  • You're integrating with Apache .htpasswd files or similar legacy formats — the htpasswd Generator uses bcrypt for a reason.

Don't use: plain SHA-256, SHA-512, MD5, or any unsalted hash. These are not password hashes. They were never meant to be.

Quick Reference

  • Both bcrypt and Argon2id are acceptable for production password hashing.
  • Argon2id is the modern choice (RFC 9106, memory-hard, winner of the PHC).
  • bcrypt is the pragmatic choice (ubiquitous, battle-tested, 25+ years of production use).
  • Cost parameters matter more than algorithm choice — target ~250 ms per hash.
  • bcrypt silently truncates at 72 bytes. Enforce a max length or pre-hash with SHA-256.
  • Always rehash on login when cost parameters are upgraded.
  • Test your hashes in the browser with the bcrypt Generator or Argon2 Generator before shipping.