Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

bcrypt vs Argon2 vs scrypt for Passwords

By Kokil Thapa | Last reviewed: September 2026

Choosing between bcrypt vs Argon2 vs scrypt for passwords is not an academic exercise. A weak or misconfigured hash is how client portals, eCommerce accounts, and API credentials get cracked after a database leak. On production Laravel and PHP systems I maintain, password hashing sits at the base of every auth stack. This guide compares the three algorithms on real criteria: memory hardness, library support, tuning knobs, and what you can ship today on PHP 8.5 and Laravel 13. For a deeper Argon2id focus, see our Argon2id vs bcrypt comparison for 2026.

Which password hashing algorithm should you choose in 2026?

Start with threat model, not hype. Offline attackers with stolen hashes want cheap parallel guessing. Online attackers hammer your login endpoint. Your algorithm must make offline cracking expensive while staying fast enough for real users.

Argon2id wins for most greenfield work in 2026. It won the Password Hashing Competition and is the current recommendation from OWASP and major language runtimes. bcrypt remains a safe legacy choice with decades of battle testing. scrypt adds memory hardness like Argon2 but sees less first-class support in PHP and Laravel today.

Password Hash Algorithm ChoiceNew app in 2026?Use Argon2idPHP 8.5 / Laravel 13Legacy bcrypt DB?Rehash on loginNode crypto only?scrypt or Argon2Never: MD5, SHA-1, plain SHA-256
Decision tree for bcrypt vs Argon2 vs scrypt for passwords in new and legacy systems

On legal-tech portals and client dashboards I have shipped, auth is only as strong as the slowest part of the stack. Pair a modern hash with rate limiting, as covered in our API rate limiting guide. Strong hashing alone does not stop online guessing.

CriterionbcryptArgon2idscrypt
Memory hardnessLow (4 KB state)High (configurable MiB)High (configurable)
GPU resistanceGoodExcellentExcellent
Side-channel resistanceModerateStrong (Argon2id hybrid)Moderate
PHP 8.5 native supportYes (PASSWORD_BCRYPT)Yes (PASSWORD_ARGON2ID)No built-in constant
Laravel 13 defaultLegacy appsRecommendedVia custom driver
Tuning parametersCost factor onlyMemory, time, threadsN, r, p, memory
2026 recommendationLegacy / compatDefault choiceNiche / Node stacks

Verdict: Argon2id for new work. bcrypt when you must interoperate with old hashes. scrypt when your runtime already standardises on it, such as some Node.js crypto workflows.

How does bcrypt work for password storage?

bcrypt is a password-based key derivation function built on Blowfish. It applies a keyed expansion loop controlled by a cost factor. Each increment doubles the work. That slow loop is the entire point.

Every bcrypt hash embeds its own salt and cost. A typical string looks like $2y$12$.... The prefix tells verifiers which variant and cost to use. You never store the salt separately.

bcrypt in PHP 8.5

<?php
$hash = password_hash('user-secret', PASSWORD_BCRYPT, ['cost' => 12]);

if (password_verify('user-secret', $hash)) {
    if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
        $hash = password_hash('user-secret', PASSWORD_BCRYPT, ['cost' => 12]);
    }
}

Cost 12 is a common 2026 baseline on a modern VPS. Target roughly 250–500 ms per hash on production hardware. Measure on your server, not your laptop. Shared hosting in Nepal often runs slower CPUs, so cost 10–11 may be the practical ceiling.

bcrypt strengths and limits

  • Strengths: Ubiquitous support, simple API, self-describing output, proven track record since the late 1990s.
  • Limits: Fixed 72-byte password input cap, low memory use that GPUs exploit, no thread parallelism inside a single hash.
  • Common mistake: Pre-hashing with SHA-256 to bypass the 72-byte limit. That turns your hash into a custom scheme and can weaken security if done wrong.

For enterprise application development, bcrypt is acceptable when compliance docs mandate it or when third-party auth libraries only emit bcrypt strings. Plan a rehash path before you paint yourself into a corner.

What makes Argon2 different from bcrypt and scrypt?

Argon2 is a memory-hard function designed to defeat ASIC and GPU farms. Unlike bcrypt, it allocates a large memory block per hash. Attackers cannot scale guesses cheaply by adding more cores alone.

Three variants exist: Argon2d (data-dependent, faster but side-channel risky), Argon2i (data-independent, weaker against offline cracking), and Argon2id (hybrid). Use Argon2id for password storage. It balances GPU resistance with side-channel protection.

Memory Hardness vs CPU Costbcrypt~4 KB RAMCPU-bound loopsArgon2id64+ MiB RAMTime + threadsHybrid side-channelBest 2026 defaultscryptConfigurable RAMN, r, p paramsStrong but nicheAttacker economicsMore RAM per guess = fewer parallel attempts on GPU farmsArgon2id and scrypt raise memory cost; bcrypt does not
bcrypt vs Argon2 vs scrypt for passwords: memory footprint and attacker cost at a glance

Argon2id in PHP 8.5

<?php
$hash = password_hash('user-secret', PASSWORD_ARGON2ID, [
    'memory_cost' => 65536,  /* 64 MiB */
    'time_cost'   => 4,
    'threads'     => 1,
]);

password_verify('user-secret', $hash);

PHP exposes memory in KiB, time as iterations, and threads for parallel lanes. The official PHP password_hash documentation lists defaults per algorithm. Tune until hashing takes a few hundred milliseconds on your production box.

Argon2id in Laravel 13

Laravel 13 ships with Argon2id-friendly defaults in config/hashing.php. Set the driver and options explicitly rather than relying on forgotten framework defaults from an older install.

/* config/hashing.php */
'argon' => [
    'memory'  => 65536,
    'threads' => 1,
    'time'    => 4,
],

Our Laravel password rehashing and Argon2id setup guide walks through upgrading bcrypt users without forced password resets. That pattern matters on long-lived portals like Mijar Law Associates, where accounts may sit untouched for months.

When should you use scrypt instead of Argon2 or bcrypt?

scrypt was designed in 2009 to make hardware cracking expensive through memory and CPU cost. Parameters N (CPU/memory cost), r (block size), and p (parallelism) control the work factor. It predates Argon2 and influenced its design.

In PHP, scrypt is not a first-class password_hash() constant. You would pull in libsodium or a vetted Composer package. That extra dependency is why most PHP teams skip scrypt today when Argon2id is already native.

Where scrypt still appears

  1. Node.js crypto: crypto.scrypt() is built in. Node.js 26 LTS projects sometimes standardise on scrypt for internal tools.
  2. Crypto wallets and blockchain-adjacent apps: Historical scrypt usage in key derivation, not always interchangeable with web password storage.
  3. Cross-language parity: When a mobile app and API already share scrypt parameters, changing algorithms requires coordinated releases.

On a greenfield API development project, I would pick Argon2id unless a spec explicitly requires scrypt. The OWASP Password Storage Cheat Sheet lists Argon2id as the preferred option when available.

Login Verify and Rehash FlowPOST loginemail + passwordLoad user rowfetch stored hashpassword_verifybcrypt or Argon2needs_rehash?weak cost / old algoSave Argon2idtransparent upgradeFailed verify: generic error, rate limit, log
Verify-then-rehash pattern for migrating bcrypt vs Argon2 vs scrypt for passwords without forced resets

How do you tune cost parameters without locking out users?

Parameter tuning is ops work, not a one-time dev task. Hashes that take two seconds on a cheap VPS will timeout under load during traffic spikes. Hashes that take 50 ms are too cheap for offline defence.

Practical targets

  • bcrypt: Cost 12 on a 2026 cloud VPS; drop to 10–11 on constrained shared hosting.
  • Argon2id: 64 MiB memory, time cost 3–4, one thread per hash on web requests.
  • scrypt: Follow RFC 7914 guidance; match memory to available RAM on the app server.

Run a micro-benchmark on production hardware after deploy. I've seen Laravel apps behave differently on Linux VPS hosting versus local Docker because CPU pinning and opcache state differ. Re-test after any hosting migration.

Never benchmark only registration. Login peaks matter more. A Dashain sale on an eCommerce site can multiply concurrent verifications. Pair tuning with queue-based registration if you expect bursts.

What never belongs in your stack

MD5, SHA-1, and unsalted SHA-256 are not password hashes. They are fast digests. An attacker can test billions per second on a single GPU. If you inherit legacy digests, force a reset or rehash at next login with a proper algorithm.

Use our password generator tool for test credentials during QA. Production passwords should come from users or a vetted manager, not predictable dev strings checked into Git.

How do you migrate from bcrypt to Argon2id safely?

Migration is a login-time upgrade, not a batch rewrite of plaintext. You do not have plaintext for existing users unless you stored it, which would be a separate disaster.

The standard pattern uses password_needs_rehash() in PHP or Laravel's Hash::needsRehash(). After a successful verify against the old bcrypt string, rehash with Argon2id and save.

/* Laravel controller excerpt */
if (Hash::check($request->password, $user->password)) {
    if (Hash::needsRehash($user->password)) {
        $user->forceFill([
            'password' => Hash::make($request->password),
        ])->save();
    }
}

Store only the hash string in the database. A column width of 255 characters covers all three algorithms. Do not truncate Argon2 output.

On client portals with document uploads, auth upgrades should sit inside your regular support and maintenance cycle. Schedule the config change, deploy, and monitor login latency for a week.

bcrypt to Argon2id Migration TimelineDay 0Deploy Argon2idconfig for new usersWeek 1–4Active users rehashon successful loginMonth 2–6Dormant accountsstill on bcryptSteady state99%+ on Argon2idOptional: email dormant bcrypt usersForce password reset only if policy or breach demands itNever store plaintext during migration
Gradual bcrypt to Argon2id migration without mass password reset emails

For WordPress 7.1 sites, core still centres on bcrypt-style phpass hashes. Custom plugins can wrap Argon2, but test plugin compatibility first. Our WordPress development service usually recommends a custom auth layer or SSO rather than patching core hashing ad hoc.

WooCommerce 11.1 inherits WordPress user tables. Treat shop customer passwords with the same upgrade discipline as your admin accounts. Checkout guest flows do not remove the need for strong hashing on registered buyers.

How does bcrypt vs Argon2 vs scrypt fit Laravel Passport, Sanctum, and API auth?

Token-based API auth does not replace password hashing. Sanctum and Passport still persist user passwords for the token issuance login step. Weak hashes undermine the entire API surface.

Read our Laravel Passport vs Sanctum comparison for auth architecture. Regardless of token type, the password column should use Argon2id in 2026.

On Quick And Easy Nepalese Grocery, customer accounts and admin APIs share one user table. One hashing policy covers web login and mobile-friendly token auth. Split policies create audit gaps.

Secrets for API keys belong in environment variables or encrypted vaults, not user password columns. See Ansible Vault for secrets for infrastructure-side handling. Application password hashing and deployment secret storage solve different problems.

Rate limit login and token endpoints. Hash strength buys time after a leak; rate limits reduce live guessing. Test under load as part of testing and optimization before launch.

Key Takeaways

  • Choose Argon2id for new PHP 8.5 and Laravel 13 applications; it beats bcrypt and scrypt on memory hardness and side-channel resistance.
  • Keep bcrypt only for legacy databases and migrate with verify-then-rehash on login, not bulk SQL updates.
  • Target 250–500 ms per hash on production hardware; measure on the server that actually runs PHP-FPM.
  • Never use MD5, SHA-1, or raw SHA-256 for passwords; use password_hash() or Laravel Hash facades only.
  • Pair strong hashing with rate limiting, HTTPS, and breach-ready logging on every auth endpoint.
  • scrypt is valid in Node.js 26 stacks but rarely worth extra PHP dependencies when Argon2id is native.

People Also Ask

Is bcrypt still secure in 2026?

Yes, bcrypt is still secure when configured with an adequate cost factor and a unique salt per password. It is no longer the best default because Argon2id offers stronger memory hardness. Existing bcrypt hashes remain safe to verify while you rehash on login.

Why do developers prefer Argon2id over Argon2i or Argon2d?

Argon2id combines data-independent and data-dependent passes. That hybrid resists both side-channel timing leaks and GPU offline cracking better than either pure variant. OWASP and PHP both treat Argon2id as the password-storage variant.

Can I use scrypt and Argon2 in the same database?

Yes. Stored hash strings are self-describing. Your verify function checks the prefix and runs the matching algorithm. After verify, rehash everything to a single target algorithm so future code stays simple.

What hash does Laravel 13 use by default?

Laravel 13 supports bcrypt and Argon2 drivers via the hashing config. New projects should set the Argon2 driver with tuned memory and time costs. Laravel 12 also supports Argon2 when PHP 8.2 or higher includes the extension.

Ship the right hash before your next auth feature

The bcrypt vs Argon2 vs scrypt for passwords debate has a practical answer for 2026: Argon2id first, bcrypt for legacy verify-and-upgrade, scrypt only when your stack already depends on it. Pick parameters on production metal, wire rehashing into login, and stop treating password columns as an afterthought.

If you are launching a portal, API, or eCommerce auth layer and want hashing, rate limits, and deployment reviewed together, contact us or explore custom software development. Read more on the blog, review portfolio projects with live auth, or learn about our work on the about page.

Frequently Asked Questions

For new PHP 8.5 and Laravel 13 apps, choose Argon2id. Keep bcrypt for legacy verify-and-upgrade. Use scrypt only when your stack already standardises on it.

Yes, with an adequate cost factor and unique salt per password. Argon2id is the better default today, but existing bcrypt hashes stay safe until you rehash on login.

Laravel 13 supports bcrypt and Argon2 drivers via config/hashing.php. New projects should explicitly set the Argon2 driver with tuned memory and time costs.

Argon2id won the Password Hashing Competition and is recommended by OWASP for password storage when available. Unlike bcrypt, it uses configurable memory hardness measured in MiB, making GPU and ASIC offline cracking far more expensive. Argon2id also offers stronger side-channel resistance through its hybrid design. PHP 8.5 exposes it natively via PASSWORD_ARGON2ID, and Laravel 13 ships Argon2-friendly defaults. bcrypt remains battle-tested but uses only about 4 KB of memory per hash, which modern GPUs exploit efficiently during parallel guessing attacks.

bcrypt is a password-based key derivation function built on Blowfish. It runs a keyed expansion loop controlled by a cost factor where each increment doubles the work. Every hash embeds its own salt and cost in a self-describing string like $2y$12$.... In PHP 8.5 you call password_hash with PASSWORD_BCRYPT and typically cost 12, then verify with password_verify. Use password_needs_rehash after verify to upgrade cost over time. Target roughly 250 to 500 milliseconds per hash on your production server, not your laptop.

Argon2 allocates a large configurable memory block per hash, making offline cracking expensive even with many GPU cores. Three variants exist: Argon2d is faster but side-channel risky, Argon2i is data-independent but weaker against offline cracking, and Argon2id combines both for password storage. bcrypt only adjusts a cost factor and uses low fixed memory. scrypt also adds memory hardness through N, r, and p parameters but lacks first-class PHP password_hash support. For most greenfield PHP work in 2026, Argon2id is the practical winner.

Pick scrypt when your runtime already standardises on it, such as Node.js 26 LTS projects using crypto.scrypt(), or when a mobile app and API already share scrypt parameters and changing algorithms requires coordinated releases. scrypt also appears in crypto wallets and blockchain-adjacent key derivation, though that is not always interchangeable with web password storage. On a greenfield PHP or Laravel API, Argon2id is the better choice because scrypt needs libsodium or a vetted Composer package while Argon2id is native in PHP 8.5.

Parameter tuning is ongoing ops work, not a one-time dev task. For bcrypt, use cost 12 on a modern 2026 cloud VPS and drop to 10 or 11 on constrained shared hosting in Nepal where CPUs are slower. For Argon2id, start with 64 MiB memory, time cost 3 to 4, and one thread per hash on web requests. Run a micro-benchmark on production hardware after every deploy or hosting migration. Never benchmark only registration; login peaks during traffic spikes like Dashain sales matter more. Hashes taking two seconds will timeout under load; hashes under 50 ms are too cheap for offline defence.

Migration happens at login time, not as a bulk SQL rewrite, because you do not have plaintext for existing users. After password_verify or Laravel Hash::check succeeds against the old bcrypt string, call password_needs_rehash or Hash::needsRehash. If true, rehash with Argon2id and save the new string. Store only the hash in a column at least 255 characters wide and do not truncate Argon2 output. Deploy the config change, monitor login latency for a week, and treat the upgrade as part of your regular maintenance cycle on long-lived client portals.

Argon2id combines data-independent and data-dependent passes in a hybrid design. That balance resists both side-channel timing leaks, which Argon2d is vulnerable to, and GPU offline cracking, where pure Argon2i is weaker. OWASP lists Argon2id as the preferred password-storage variant, and PHP exposes it as PASSWORD_ARGON2ID rather than the other variants. For web application password columns where attackers may observe timing and also steal hash dumps, Argon2id is the variant purpose-built for that dual threat model.

Yes. Stored hash strings from bcrypt, Argon2, and scrypt are self-describing because each embeds its own algorithm prefix, salt, and parameters. Your verify function reads the prefix and runs the matching algorithm automatically through password_verify or Laravel Hash::check. After a successful verify, rehash everything to a single target algorithm such as Argon2id so future application code stays simple and audit-friendly. Keep the password column at 255 characters to accommodate any of the three formats without truncation.

No. PHP 8.5 provides native PASSWORD_BCRYPT and PASSWORD_ARGON2ID constants through password_hash, but scrypt is not a built-in password_hash constant. Implementing scrypt in PHP means pulling in libsodium or a vetted Composer package, which adds dependency and review overhead. That extra step is why most PHP teams skip scrypt when Argon2id is already available natively. scrypt remains a valid choice in Node.js 26 LTS where crypto.scrypt is built in, but for Laravel 13 and PHP 8.5 greenfield work, Argon2id is the simpler path.

MD5, SHA-1, and unsalted SHA-256 are fast digests, not password hashes. An attacker with a stolen database can test billions of guesses per second on a single GPU because there is no deliberate slowdown or meaningful memory cost. Proper algorithms like bcrypt and Argon2id embed a unique salt and tunable work factors specifically to make offline cracking expensive. If you inherit legacy MD5 or SHA digests, force a password reset or rehash at next login with password_hash. Never pre-hash with SHA-256 to bypass bcrypt's 72-byte limit unless you fully understand the security trade-offs.

Token-based API auth does not replace password hashing. Sanctum and Passport still persist user passwords for the token issuance login step, so weak hashes undermine the entire API surface regardless of token type. On production apps where customer accounts and admin APIs share one user table, one hashing policy should cover web login and mobile-friendly token auth; split policies create audit gaps. Set Argon2id in config/hashing.php, rate limit login and token endpoints, and remember that hash strength buys time after a leak while rate limits reduce live guessing.

bcrypt caps password input at 72 bytes, uses only about 4 KB of memory per hash making GPU parallel guessing cheaper than against Argon2id, and offers no thread parallelism inside a single hash computation. It remains ubiquitous with decades of battle testing and simple tuning via one cost factor, which is why compliance docs and third-party auth libraries still mandate it. Accept bcrypt when legacy databases or interoperability require it, but plan a verify-then-rehash migration path to Argon2id before new features depend on bcrypt-specific behaviour. Avoid pre-hashing with SHA-256 to bypass the byte limit without expert review.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: