← Back to Blog

Bcrypt vs Argon2 in 2026: Which Password Hash Is More Secure?

Every week, another database gets breached and millions of password hashes leak online. The difference between bcrypt and Argon2 is the difference between passwords cracked in days and passwords that stay safe for years. Here is everything you need to know to make the right choice.

Why Fast Hash Functions Are Dangerous for Passwords

Developers new to security often reach for SHA-256 or MD5 to hash passwords because those functions are fast, well known, and built into every language's standard library. That speed is exactly the problem. Modern GPUs can compute 10–50 billion SHA-256 hashes per second. A leaked database of SHA-256 password hashes can be cracked in minutes using commodity hardware and a dictionary of common passwords.

Password hashing algorithms solve this by being intentionally slow and expensive. They are designed so that verifying one legitimate login takes ~100–300ms (imperceptible to the user) while brute-forcing billions of guesses becomes computationally prohibitive. This is called key stretching.

The two most widely recommended algorithms today are bcrypt and Argon2. Both are far better than MD5, SHA-256, or SHA-512 for password storage. But they differ significantly in design, security properties, and when you should use each.

Bcrypt: The Battle-Tested Standard

Bcrypt was designed by Niels Provos and David Mazières in 1999 and published at USENIX. It is based on the Blowfish block cipher and was the first widely adopted password hashing function designed explicitly to be slow and adjustable. Its core innovation was the cost factor - a single integer that controls how much work is done, and can be increased as hardware gets faster.

A bcrypt hash looks like this:

$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewdBPj6ZkBU9WvWi

Breaking that down:

  • $2b$ - algorithm version (2b is current)
  • 12 - cost factor (212 = 4,096 iterations)
  • Next 22 characters - base64-encoded 128 bit salt (automatically generated)
  • Remaining 31 characters - the hash output

Bcrypt Strengths

  • Proven track record: 25+ years in production without fundamental breaks
  • Automatic salting: Salt is embedded in the output, preventing rainbow table attacks
  • Adjustable cost: Increment the cost factor as CPUs speed up
  • Universal support: Available in every major language and framework
  • Simple API: Most libraries reduce it to two functions: hash() and verify()

Bcrypt Weaknesses

  • 72-byte password truncation: Bcrypt only processes the first 72 bytes of input. Passwords longer than 72 bytes are silently truncated. "correcthorsebatterystaple_with_extra_padding_here" and that same string with 100 more characters will produce the same hash.
  • CPU-only hardness: Bcrypt is computationally expensive but not memory-expensive. Modern GPUs can still run many parallel bcrypt computations because the memory footprint is small (~4KB). A high-end GPU can compute ~100k–200k bcrypt hashes per second at cost factor 12.
  • No parallelism parameter: You cannot easily tune bcrypt for multi-core systems in a way that is also expensive for attackers.

Argon2: The Modern Successor

Argon2 won the Password Hashing Competition (PHC) in 2015, a rigorous multi-year public evaluation process. It was designed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich at the University of Luxembourg. Unlike bcrypt, Argon2 was designed with modern attack hardware in mind, particularly GPUs and ASICs.

Argon2 comes in three variants:

  • Argon2d: Maximizes resistance to GPU attacks by using data-dependent memory access. Vulnerable to side-channel attacks, so not recommended for password hashing.
  • Argon2i: Uses data-independent memory access, safe against side-channel attacks. Better for key derivation but slightly weaker against GPU attacks than Argon2d.
  • Argon2id: Hybrid of Argon2d and Argon2i. This is the recommended variant for password hashing. It provides the best balance of GPU resistance and side-channel safety. OWASP, NIST, and RFC 9106 all recommend Argon2id.

An Argon2id hash looks like:

$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG

The parameters embedded in the hash string are:

  • m=65536 - memory cost in kilobytes (64 MB here)
  • t=3 - time cost (number of iterations)
  • p=4 - parallelism (threads)

Argon2 Strengths

  • Memory hardness: Argon2 requires a configurable amount of RAM to compute. Because GPUs have limited memory per core and memory is shared across cores, memory-hard algorithms are dramatically more expensive to run in parallel on GPUs. This is the most important security advance over bcrypt.
  • Three tunable parameters: Memory, time, and parallelism can be tuned independently to match your hardware constraints while maximizing attacker cost.
  • No input length limit: Unlike bcrypt, Argon2 handles passwords of any length correctly.
  • Modern design: Specifically engineered against GPU and ASIC attacks using lessons from the 2010s hardware landscape.
  • RFC standardized: Formally specified in RFC 9106 (2021).

Argon2 Weaknesses

  • Less universal support: Older frameworks (PHP < 7.2, older Python, legacy Java) do not have native Argon2 and require a third-party library.
  • Memory requirements on server: If you hash at 64MB memory and handle 100 concurrent logins, you need 6.4GB of RAM just for hashing. This must be factored into server sizing.
  • More complex tuning: Three parameters instead of one. Misconfiguration (e.g., too little memory) can reduce security.

Side-by-Side Comparison

PropertyBcryptArgon2id
Year introduced19992015 (PHC winner)
Memory hardnessNo (~4KB)Yes (configurable, 64MB+ recommended)
GPU resistanceModerateHigh
ASIC resistanceLowHigh
Password length limit72 bytesNone
Tuning parameters1 (cost factor)3 (memory, time, parallelism)
OWASP recommendedYes (fallback)Yes (first choice)
RFC/NIST standardizedNoRFC 9106
Framework supportUniversalBroad but not universal

Real-World Examples: Implementation in Code

Step 1: Choose recommended parameters

For Argon2id, OWASP recommends (as of 2026): 64MB memory, 3 iterations, 1 thread minimum (or 19MB memory with 2 iterations as a lower-memory alternative). For bcrypt, OWASP recommends cost factor 10–12 (12 is widely used and takes ~250ms on a modern server).

Step 2: Hash a password (Node.js examples)

// Argon2id - install: npm install argon2
const argon2 = require('argon2');

async function hashPassword(password) {
  return await argon2.hash(password, {
    type: argon2.argon2id,
    memoryCost: 65536,   // 64MB in KB
    timeCost: 3,         // 3 iterations
    parallelism: 1,
  });
}

async function verifyPassword(hash, password) {
  return await argon2.verify(hash, password);
}

// Bcrypt - install: npm install bcrypt
const bcrypt = require('bcrypt');

async function hashPasswordBcrypt(password) {
  const saltRounds = 12;
  return await bcrypt.hash(password, saltRounds);
}

async function verifyPasswordBcrypt(password, hash) {
  return await bcrypt.compare(password, hash);
}

Step 3: Hash a password (Python examples)

# Argon2id - install: pip install argon2-cffi
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError

ph = PasswordHasher(
    time_cost=3,
    memory_cost=65536,  # 64MB
    parallelism=1,
    hash_len=32,
    salt_len=16
)

def hash_password(password: str) -> str:
    return ph.hash(password)

def verify_password(hash: str, password: str) -> bool:
    try:
        return ph.verify(hash, password)
    except VerifyMismatchError:
        return False

# Bcrypt - install: pip install bcrypt
import bcrypt

def hash_password_bcrypt(password: str) -> bytes:
    return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=12))

def verify_password_bcrypt(password: str, hash: bytes) -> bool:
    return bcrypt.checkpw(password.encode('utf-8'), hash)

Step 4: PHP (built-in since PHP 7.2)

// Argon2id - built in since PHP 7.2
$hash = password_hash($password, PASSWORD_ARGON2ID, [
    'memory_cost' => 65536,  // 64MB
    'time_cost'   => 3,
    'threads'     => 1,
]);

$valid = password_verify($password, $hash);

// Bcrypt - built in since PHP 5.5
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
$valid = password_verify($password, $hash);

Generate and Verify Bcrypt Hashes Instantly

Use our free browser based Bcrypt Generator - hash a password or verify an existing hash without any data leaving your browser.

Open Bcrypt Generator →

Migration: Moving from Bcrypt to Argon2id

You do not need to force all users to reset their passwords. The standard migration pattern is opportunistic re-hashing:

  1. On successful login, check if the stored hash uses bcrypt (e.g., starts with $2b$)
  2. If yes, re-hash the plaintext password with Argon2id and update the stored hash
  3. Over time, all active users will have their hashes upgraded transparently
  4. Inactive users keep bcrypt hashes until their next login (still safe, just not upgraded)
async function loginUser(inputPassword, storedHash) {
  let isValid = false;

  if (storedHash.startsWith('$2b$') || storedHash.startsWith('$2a$')) {
    // Legacy bcrypt hash
    isValid = await bcrypt.compare(inputPassword, storedHash);
    if (isValid) {
      // Upgrade to Argon2id
      const newHash = await argon2.hash(inputPassword, { type: argon2.argon2id });
      await updateHashInDatabase(newHash);
    }
  } else {
    // Modern Argon2id hash
    isValid = await argon2.verify(storedHash, inputPassword);
  }

  return isValid;
}

What About PBKDF2 and scrypt?

PBKDF2 is FIPS-140 compliant and required in some regulated environments (healthcare, government). It is acceptable but inferior to both bcrypt and Argon2 in terms of GPU resistance because it has no memory hardness. Use it only when regulatory compliance demands it.

scrypt was designed to be memory-hard before Argon2 existed. It is a solid choice but harder to tune correctly and has less framework support than Argon2id. Argon2id is preferred for new projects.

OWASP recommendation (2026): Use Argon2id with m=65536 (64MB), t=3, p=1 as first choice. Use bcrypt with cost factor 10+ as an acceptable alternative when Argon2 is unavailable.

Frequently Asked Questions

Is bcrypt still safe to use in 2026?

Yes, bcrypt at cost factor 12+ is still considered secure for password hashing in 2026. No fundamental cryptographic weakness has been found in 25 years. It is not as GPU-resistant as Argon2id due to its lack of memory hardness, but with a high enough cost factor it remains a reasonable choice, especially in environments where Argon2 is not available or not yet supported by the framework.

Why can't I use SHA-256 with a salt for passwords?

Even with a random salt, SHA-256 is far too fast for password hashing. A modern GPU can compute 10+ billion salted SHA-256 hashes per second. That means an 8-character lowercase password (268 = ~208 billion combinations) would be exhausted in under a minute. Bcrypt cost factor 12 reduces that to ~50 years equivalent. The salt prevents precomputed (rainbow table) attacks, but it does not slow down brute force. Only a deliberately slow algorithm does that.

What is the 72-byte bcrypt limit and does it matter?

Bcrypt internally uses Blowfish's key schedule, which accepts at most 72 bytes. Any characters beyond the 72nd byte are silently ignored. This is rarely a real-world problem because most users have passwords well under 72 bytes. However, if you plan to allow passphrases or API keys as passwords, pre-hashing with SHA-256 before bcrypt (and then base64-encoding) is a common workaround: bcrypt(base64(sha256(password))). Argon2id has no such limit.

How do I pick Argon2id parameters?

Start with OWASP's recommended baseline: m=65536, t=3, p=1. Then benchmark on your actual server: the hash operation should take 200–500ms. If it is too slow, reduce memory (try m=19456 with t=2). If your server has ample RAM and you want more security margin, increase memory. The key insight is that increasing memory cost hurts GPU attackers far more than it hurts your server, because GPUs have limited per-core memory.

Should I pepper passwords in addition to hashing?

A pepper is a secret value stored in the application's environment (not in the database) that is combined with the password before hashing. It adds an extra layer: even if an attacker steals the database, they cannot crack hashes without also compromising the application server (to get the pepper). This is a useful defense in depth measure. Implement it as hash = Argon2id(password + pepper) or hash = HMAC-SHA256(Argon2id(password), pepper). Keep the pepper in an environment variable or secrets manager, never in the database.

Does Argon2id work with existing login forms?

Yes, completely transparently. The user types their password in plain text into the login form (over HTTPS), and your server hashes it. The algorithm switch is entirely server side. Users never know or care whether you use bcrypt or Argon2id - the login flow is identical. The only change is in your server side password hashing and verification code.

The Bottom Line

For any new project started in 2026, use Argon2id with at least 64MB memory cost, 3 iterations, and 1 degree of parallelism. It is the formally standardized, GPU-resistant, memory-hard algorithm recommended by OWASP, NIST, and security researchers worldwide. If your framework does not support Argon2, use bcrypt at cost factor 12 - it remains secure. Never use MD5, SHA-1, or SHA-256 for passwords.

The investment in proper password hashing is one of the highest-ROI security decisions you can make. A data breach is devastating; a breach where all passwords are properly hashed with Argon2id is recoverable.

Use our free tool here → Bcrypt Generator & Verifier to hash and verify passwords instantly in your browser.

UK
Written by Usman Khan
DevOps Engineer | MSc Cybersecurity | CEH | AWS Solutions Architect

Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.