Over the years, while building backend systems and maintaining several online cryptography tools, password hashing is one area where I've seen the most confusion - even among experienced developers.
Like many Java developers, my journey started with bcrypt, mostly because it is the default choice in Spring Security. It worked, it was easy to plug in, and most tutorials treated it as "the secure option".
But as I started digging deeper - reading security recommendations, answering user questions, and building hash generators - I realized that choosing a password hashing algorithm is less about "what's popular" and more about understanding the threat model.
This article is a practical comparison of Argon2, bcrypt, and scrypt, written from the perspective of someone who has implemented them, misused them, fixed mistakes, and built tools around them.
My Starting Point: bcrypt in Spring Security
If you've used Spring Security, you already know how easy bcrypt is:
PasswordEncoder encoder = new BCryptPasswordEncoder();
String hash = encoder.encode(password);
No parameters to worry about. No memory settings. Just a cost factor.
Bcrypt was designed to be slow and adaptive, which already made it far better than older hash functions like SHA-1 or MD5. For years, it was the right answer.
But while building a bcrypt hash in Spring Security and reading user comments, I noticed two recurring problems:
- Developers using very low cost factors
- Developers assuming bcrypt is GPU-resistant
That's when I started looking deeper into why newer algorithms exist.
Why Password Hashing Is NOT Encryption
One of the most common mistakes I see is developers confusing password hashing with encryption.
Encryption is reversible - you encrypt data and later decrypt it. Password hashing is intentionally one-way. You should never be able to retrieve the original password.
This difference matters because:
- Encrypted passwords can be stolen and decrypted
- Hashed passwords must be brute-forced
Once you understand this, the goal becomes clear: make brute-force attacks as expensive as possible.
Why GPU Attacks Changed Everything
Modern password cracking is not done on CPUs. It's done on GPUs and ASICs.
Bcryptis CPU-hard, but it is not strongly memory-hard. That means attackers can still scale attacks using specialized hardware.
This is the root problem that led to scrypt and later Argon2.
Once you understand this, the "bcrypt vs Argon2" discussion suddenly makes sense.
Scrypt: My First Step Beyond bcrypt
Scrypt was one of the first widely adopted attempts at memory-hard password hashing. It introduced parameters like N, r, and p, which directly control memory and CPU usage.
When I implemented a scrypt hash generator, I realized something important:
scrypt is powerful, but very easy to misconfigure.
- Too low memory -> weak protection
- Too high memory -> application crashes
This is where many developers struggle. scrypt improved security, but usability suffered.
Discovering Argon2: The Missing Piece
I first encountered Argon2 while reading modern OWASP recommendations and RFC discussions. Argon2 won the Password Hashing Competition (PHC), and that alone made me curious.
When I implemented an Argon2 hash generator, the design differences were immediately obvious:
- Explicit memory cost
- Explicit time cost
- Parallelism control
More importantly, Argon2 was designed from day one to resist both GPU and ASIC attacks.
This is why many modern frameworks and security teams now recommend Argon2id as the default choice for new applications.
Quick, Practical Overview of Each Algorithm
bcrypt
bcrypt is CPU-hard and adaptive. It was a massive improvement over SHA-based hashing and is still widely used today.
I've used bcrypt extensively in Spring Security and even built a bcrypt hash generator to help developers understand cost factors.
Its biggest limitation is that it is not strongly memory-hard.
scrypt
scrypt was one of the first mainstream attempts to introduce memory hardness into password hashing.
When I implemented a scrypt hash generator, I quickly realized that its flexibility is both a strength and a weakness.
The parameters (N, r, p) give you control, but also make misconfiguration very easy.
Argon2
I first learned about Argon2 while reading OWASP recommendations and RFC discussions. Argon2 won the Password Hashing Competition (PHC), and that alone made me pay attention.
Once I built an Argon2 hash generator and verifier, the design differences were obvious.
Argon2 explicitly separates:
- Memory cost
- Time cost
- Parallelism
This makes it far more resistant to modern hardware attacks.
Argon2 vs bcrypt vs scrypt: Practical Comparison
| Aspect | Argon2 | bcrypt | scrypt |
|---|---|---|---|
| Memory Hard | Yes (strong) | Limited | Yes |
| GPU / ASIC Resistance | Excellent | Moderate | Good |
| Configurability | High | Low | High |
| Ease of Correct Usage | Medium | High | Low |
| Modern Recommendation | Yes | Legacy-safe | Conditional |
When to Use Each Algorithm
Use Argon2 When
- You are building a new application
- You control server memory
- You want long-term security
Use bcrypt When
- You are tied to existing frameworks like Spring Security
- You need maximum ecosystem compatibility
- You cannot risk breaking authentication flows
Use scrypt When
- You need memory hardness
- Argon2 is unavailable
- You understand parameter tuning well
When NOT to Use Each Algorithm
Do NOT Use bcrypt
- When defending against GPU-heavy attackers
- When long-term security is critical
Do NOT Use scrypt
- If you don't understand N, r, and p
- If memory usage can crash your app
Do NOT Use Argon2
- In extremely memory-constrained environments
- When libraries lack proper support
Real-World Usage I've Seen
- bcrypt - Spring Security, legacy Java apps
- scrypt - older security-focused systems
- Argon2 - modern frameworks and security-first apps
Most new recommendations today favor Argon2id for password storage.
Try the Algorithms Yourself
Using the tools side-by-side makes the differences very obvious.
Migration Guidance: bcrypt -> Argon2
One important thing I learned: you don't migrate password hashes all at once.
- Keep bcrypt verification for existing users
- Re-hash with Argon2 on successful login
- Gradually phase out bcrypt
This approach avoids forced password resets and keeps user experience intact.
Final Thoughts
I started with bcrypt because it was easy and widely recommended. I moved to Argon2 after understanding how modern attacks actually work.
That progression mirrors the broader industry shift.
The best password hashing algorithm is not the trendiest one - it's the one you understand, configure correctly, and maintain over time.