
September 09, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Choosing between Password Hashing Argon2id vs Bcrypt in 2026 is not a theoretical debate for PHP teams. Every login form, client portal, and API token store depends on a slow hash that survives GPU cracking if the database leaks. On production Laravel applications I maintain, the wrong default or stale bcrypt cost can leave credentials exposed for years. This guide compares Argon2id and bcrypt on current PHP 8.5 and Laravel 13 stacks, with copy-paste config, migration steps, and a practical verdict. For deeper Laravel-specific rehashing, see the Laravel password rehashing and Argon2id setup guide.
What Is the Difference Between Argon2id and Bcrypt for Password Hashing?
Both algorithms turn a plaintext password into a one-way string. Neither can be reversed. An attacker who steals your user table must guess passwords and re-hash each guess until one matches.
Bcrypt, based on Blowfish, has been the PHP default for over a decade. It uses a cost factor that scales CPU time. Argon2 won the Password Hashing Competition in 2015. Argon2id blends Argon2i (side-channel resistance) and Argon2d (GPU resistance). That hybrid makes it the stronger choice on modern hardware.
The critical property is slowness. Login hashing should take roughly 200–500 ms per attempt on your production server. Fast hashes like SHA-256 let attackers test billions of guesses per second. Slow hashes cap that rate to hundreds or thousands.
PHP stores algorithm metadata inside the hash string itself. A bcrypt hash starts with $2y$. An Argon2id hash starts with $argon2id$. That prefix lets password_verify() pick the correct algorithm at login time.
How each algorithm resists attacks
- Bcrypt: Adaptive work factor via the cost parameter (typically 10–12 in 2026).
- Argon2id: Three tunable dimensions — time cost, memory cost, and parallelism.
- Both: Per-password random salt embedded in the output string.
- Neither: Suitable for session tokens or API keys — use random bytes and a fast MAC for those.
Which Password Hash Should You Use in PHP 8.5 and Laravel 13?
PHP 8.5 ships with Argon2id as the default PASSWORD_DEFAULT algorithm. Laravel 13 follows that direction in fresh installs. If your VPS runs Ubuntu 24 with PHP 8.5 and the sodium extension enabled, Argon2id is the right primary choice.
Bcrypt stays valid when Argon2 is unavailable. Some budget shared hosts still compile PHP without sodium support. In those cases, bcrypt at cost 12 is far better than leaving legacy MD5 hashes in place.
Laravel 13 hashing configuration
In config/hashing.php, set the driver explicitly rather than relying on silent defaults:
<?php
return [
'driver' => env('HASH_DRIVER', 'argon2id'),
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 12),
'verify' => true,
],
'argon' => [
'memory' => 65536, // 64 MiB in KiB
'threads' => 1,
'time' => 4,
'verify' => true,
],
];
Run php artisan config:cache after changing values on production. I've seen stale config leave new installs on bcrypt while developers assumed Argon2id was active. Clear the config cache as part of every deploy pipeline.
Plain PHP without a framework
<?php
$hash = password_hash(
$password,
PASSWORD_ARGON2ID,
['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 1]
);
if (password_verify($password, $hash)) {
if (password_needs_rehash($hash, PASSWORD_ARGON2ID, $options)) {
$hash = password_hash($password, PASSWORD_ARGON2ID, $options);
/* persist upgraded hash */
}
}
The official PHP manual documents every option flag for password_hash(). Cross-check your values there before pushing to production. The PHP password_hash documentation remains the authoritative reference.
How Do Argon2id and Bcrypt Compare on Security and Performance?
The OWASP Password Storage Cheat Sheet recommends Argon2id as the first preference when available. Bcrypt is listed as an acceptable alternative. That ranking reflects real attack economics, not vendor marketing.
| Criterion | Argon2id | Bcrypt |
|---|---|---|
| GPU / ASIC resistance | Strong — memory-hard design | Moderate — CPU-only scaling |
| Side-channel resistance | Strong (hybrid i+d variant) | Good for most web apps |
| Max password length | Practically unlimited | 72 bytes (pre-hash if longer) |
| PHP 8.5 support | Native via sodium / PASSWORD_ARGON2ID | Native via PASSWORD_BCRYPT |
| Hosting compatibility | Needs sodium extension | Works almost everywhere |
| Tuning dimensions | Memory, time, threads | Cost rounds only |
| 2026 industry default | Preferred for new systems | Legacy-safe, still valid |
On a 2-vCPU VPS with 4 GB RAM — common for Nepal SMB sites at Rs 1,500–3,000/month (~USD 11–22) — Argon2id at 64 MiB and time cost 4 typically lands near 300 ms. Bcrypt at cost 12 runs closer to 250 ms on the same box. The difference is negligible at login but meaningful against offline cracking.
Memory hardness is the decisive edge. A bcrypt hash on a stolen dump can be attacked with thousands of GPU cores. Argon2id forces each guess to allocate tens of megabytes of RAM. That bottleneck shrinks parallel cracking throughput by orders of magnitude.
Do not confuse password hashing with general encryption. Hashing is one-way verification. For document storage on legal-tech portals, use proper encryption at rest plus access controls. I've built client portals where password hashing and file encryption serve different layers of the same security model.
Benchmark on your own server
- SSH into production or a staging mirror with identical PHP-FPM settings.
- Run a short script that hashes a test password 20 times and prints average milliseconds.
- Adjust Argon2id memory or bcrypt rounds until the average sits between 200 and 500 ms.
- Re-test under load — run the script while Apache or Nginx serves normal traffic.
- Document the final values in your deploy notes and
.env.example.
Server tuning affects hash timing as much as algorithm choice. PHP-FPM worker counts, opcache settings, and concurrent login spikes all matter. The Ubuntu server setup guide for PHP apps covers baseline VPS hardening that keeps auth endpoints stable.
How Should You Migrate Existing Bcrypt Hashes to Argon2id?
Never bulk-rehash the entire user table offline. You do not have plaintext passwords. The correct pattern is lazy rehashing at successful login.
Laravel's built-in authentication already supports this flow when you call Hash::needsRehash() after verification. Symfony and plain PHP use password_needs_rehash() the same way. On a legal-tech portal with thousands of dormant accounts, only active users get upgraded — and that is fine.
Migration workflow
- Confirm sodium and Argon2id support:
php -r "echo PASSWORD_ARGON2ID;" - Update
config/hashing.phpdriver toargon2id. - Add rehash logic in your login controller or Laravel user provider.
- Deploy during low-traffic hours and monitor PHP-FPM memory usage.
- Keep bcrypt verification working — old hashes must still validate until rehashed.
WordPress sites follow a different path. Core still defaults to bcrypt-based phpass unless a plugin switches algorithms. The WordPress security hardening checklist covers plugin-level upgrades without breaking existing logins.
Client portals like Mijar Law Associates store sensitive credentials alongside document access. A botched migration that invalidates passwords generates support tickets and erodes trust. Test the rehash path on staging with cloned production data before you deploy.
What Common Password Hashing Mistakes Still Appear in Production?
Even teams that know better slip on implementation details. These failures show up in audits, penetration tests, and incident post-mortems.
Using fast hashes or reversible encoding
MD5, SHA-1, and SHA-256 were never designed for passwords. Salting a SHA-256 hash does not fix the speed problem. Base64 is encoding, not protection. If you inherit a legacy CodeIgniter or custom PHP app with unsalted MD5, plan an incremental upgrade rather than a big-bang rewrite.
Ignoring the bcrypt 72-byte limit
Passwords longer than 72 bytes are truncated silently by bcrypt. Pre-hash with SHA-256, then bcrypt the digest, if you must support passphrases. Argon2id avoids this footgun entirely. Test edge cases with the secure password generator tool to confirm your app handles long random strings.
Setting Argon2id memory too high on small VPS plans
A 256 MiB memory cost on a 1 GB RAM droplet can exhaust PHP-FPM workers during a login burst. Start at 64 MiB. Measure. Scale up only if headroom exists. Linux system administration support often starts with exactly this kind of memory profiling.
Skipping rate limiting and MFA
Strong hashing protects offline dumps. It does not stop online guessing against a live login form. Pair slow hashes with rate limiting, CAPTCHA after failures, and MFA for admin roles. The 2026 cybersecurity trends for developers article covers layered auth defence beyond hashing alone.
Hard-coding pepper without a rotation plan
A pepper — a server-side secret mixed before hashing — adds defence in depth. Losing the pepper invalidates every password. Store peppers in environment variables, not Git. Document rotation as a manual rehash project because you cannot recover plaintext.
API-only backends need the same discipline. JWTs are not password stores. Hash credentials in your user service and issue short-lived tokens after verification. The REST API design best practices guide treats auth as a first-class architectural concern.
How Does Password Hashing Fit Into Broader Application Security?
Hashing is one layer in a stack that includes TLS, CSRF protection, session fixation guards, and secure cookie flags. On Court Marriage In Nepal and similar lead-capture portals, most users create passwords once and log in rarely. That usage pattern makes lazy rehashing ideal — returning visitors upgrade silently over months.
Enterprise apps with SSO may still store local passwords for fallback accounts. Keep those hashes on the same modern standard as primary credentials. Symfony 8.1 applications can configure the password_hasher service per user type — bcrypt for legacy imports, Argon2id for new records.
Compliance conversations in Nepal often reference international baselines rather than a single local password statute. Following the OWASP Password Storage Cheat Sheet satisfies most client security questionnaires I've seen on RFPs for enterprise application development work.
Testing belongs in CI. Add a PHPUnit or Pest test that asserts new registrations produce $argon2id$ prefixes. Add another that feeds a known bcrypt fixture through login and confirms the stored hash upgrades. Testing and optimization services often start with exactly these auth regression gaps.
Founders choosing a stack for a 2026 greenfield product should default to Laravel 13 or Symfony 8.1 on PHP 8.5 with Argon2id. The case for learning Laravel in 2026 includes built-in hashing conventions that save weeks of security plumbing.
If you maintain WooCommerce or Magento 2.4.x storefronts, customer passwords follow platform defaults unless you override them. Custom Laravel carts — like those built for Quick And Easy Nepalese Grocery — give you full control. Use it.
Key Takeaways
- Prefer Argon2id on PHP 8.5 and Laravel 13 when sodium is available; bcrypt at cost 12 remains a safe fallback.
- Tune hash duration to 200–500 ms on production hardware — measure, do not guess memory or cost values.
- Migrate existing bcrypt hashes with lazy rehash at login via
password_needs_rehash(), never offline bulk conversion. - Pair slow hashing with rate limiting, MFA for privileged roles, and TLS — hashing alone does not stop online attacks.
- Test registration and login paths in CI to catch algorithm regressions before deploy.
- Avoid MD5, SHA-256, and Base64 for password storage — they fail the offline cracking test immediately.
People Also Ask
Is Argon2id better than bcrypt in 2026?
Yes, for most new PHP applications. Argon2id's memory-hard design resists GPU-based offline cracking better than bcrypt. Bcrypt remains acceptable when Argon2 is unavailable or when you need maximum hosting compatibility without code changes.
What is the default password algorithm in PHP 8.5?
PHP 8.5 uses Argon2id as PASSWORD_DEFAULT. Calling password_hash($pw, PASSWORD_DEFAULT) produces an Argon2id hash when the sodium extension is compiled in. Verify with php -i | grep -i argon on your server.
Can Laravel verify old bcrypt passwords after switching to Argon2id?
Yes. Laravel's hasher detects the algorithm from the hash prefix. Bcrypt hashes continue to verify correctly. After login, Hash::needsRehash() returns true and you store a fresh Argon2id hash.
How long should password hashing take?
Target 200–500 milliseconds per hash on production hardware. Faster hashes weaken offline attack resistance. Slower hashes risk denial-of-service during traffic spikes and frustrate legitimate users at login.
Choose Argon2id, Keep Bcrypt as Fallback, and Ship Secure Logins
Password Hashing Argon2id vs Bcrypt in 2026 is settled for greenfield work: Argon2id wins on modern PHP stacks. Bcrypt is not broken — it is the pragmatic choice on constrained hosting and the bridge that keeps legacy logins working during migration. Pick Argon2id for new Laravel 13 and Symfony 8.1 projects, tune parameters on real hardware, and rehash bcrypt rows lazily at login.
Need auth hardened on an existing portal or a new custom software project? I help teams audit credential storage, configure hashing, and deploy without breaking user sessions. Contact us to review your login stack, or browse the Notary Nepal portfolio for an example of secure client-facing auth in production.
Frequently Asked Questions
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.

