Blog
bcrypt or argon2: Picking the Right Password Hash in 2026
Storing passwords safely means using a slow, salted, memory-hard hash designed for the job, so this is the case for choosing between bcrypt and argon2 and configuring either correctly.
Passwords are the credential developers handle most often and protect worst. The instinct to reach for SHA-256 or, worse, MD5 is exactly wrong, because those hashes are fast, and fast is the enemy when an attacker has stolen your database and wants to crack hashes offline. Password hashing needs to be deliberately slow and resource-hungry so that testing each guess costs real time and memory. Two algorithms dominate the modern landscape: bcrypt, the battle-tested workhorse that has guarded passwords for over two decades, and argon2, the newer memory-hard winner of the Password Hashing Competition. Both are good choices, but they have different tuning knobs and different threat resistances. This article explains why general-purpose hashes fail, how salting and work factors defend you, and how to pick and configure bcrypt or argon2 for a Node backend without shooting yourself in the foot.
Why Fast Hashes Lose
A cryptographic hash like SHA-256 is designed to be fast, because it is meant for verifying file integrity and signing where speed is a virtue. That speed is catastrophic for passwords. Modern hardware can compute billions of SHA-256 hashes per second on a single GPU, and rigs with many cards push that into the trillions. If an attacker steals your user table and the passwords are SHA-256 hashes, they can run an offline cracking campaign testing enormous dictionaries and common patterns almost instantly, recovering most real-world passwords because people choose predictable ones. Password hashing algorithms invert this logic on purpose: they are deliberately slow and tunable so each guess costs meaningful time. A hash that takes a quarter of a second to compute is invisible to a user logging in once, but it reduces an attacker's guessing rate from billions per second to a handful, which changes the economics entirely.
Salting Defeats Precomputation
A salt is a unique random value generated per password and mixed into the hash. Without salting, identical passwords produce identical hashes, which means an attacker can precompute a giant table mapping common passwords to their hashes, a rainbow table, and then look up every matching hash in your database at once. Salting destroys this attack because the same password hashed with two different salts produces two completely different outputs, so a precomputed table is useless and the attacker must crack each hash individually. The salt is not secret; it is stored alongside the hash, and that is fine, because its job is uniqueness rather than concealment. The good news is that both bcrypt and argon2 generate and embed the salt automatically. The resulting hash string contains the algorithm identifier, the parameters, the salt, and the digest, so you store one self-describing string and never manage salts manually.
How bcrypt Works
bcrypt is built on the Blowfish cipher and has protected passwords since 1999, which is a feature rather than a sign of age, because decades of scrutiny have found no practical breaks. Its key parameter is the cost factor, an exponent that doubles the work each time you increment it. A cost of twelve runs the underlying key schedule four thousand and ninety-six times, taking a fraction of a second on current hardware. You raise the cost as machines get faster, keeping the per-hash time roughly constant over the years. bcrypt has two quirks to remember. First, it silently truncates input beyond seventy-two bytes, so very long passwords or passphrases lose their tail, which matters if you also pre-hash. Second, it embeds everything you need to verify in the output string, so verification re-reads the cost and salt from the stored hash. For most applications bcrypt remains a defensible default.
How argon2 Works
argon2 won the Password Hashing Competition in 2015 and addresses a weakness in bcrypt: bcrypt is hard on CPU time but uses little memory, so attackers can build cheap massively parallel cracking hardware. argon2 is memory-hard, meaning each hash deliberately consumes a configurable amount of RAM, which makes the parallel GPU and ASIC attacks that hammer fast hashes far more expensive because memory does not scale as cheaply as raw compute. It has three knobs: memory cost, the amount of RAM per hash; time cost, the number of iterations; and parallelism, the number of threads. The recommended variant for password storage is argon2id, which blends resistance to both side-channel attacks and time-memory trade-off attacks. If you are starting a new project today and your platform has a well-maintained argon2 binding, it is the marginally stronger choice, particularly against adversaries with specialized hardware budgets.
Tuning The Work Factor
Picking parameters is a balance between security and user experience, and the right answer depends on your hardware, not a number copied from a blog post. The standard guidance is to tune so that hashing a single password takes somewhere between roughly two hundred and five hundred milliseconds on your production servers. Below that and an attacker cracks too fast; above it and your login latency and CPU bill climb, and a burst of logins can exhaust your server under load. Measure on the actual machine you deploy to, because a developer laptop and a constrained container behave very differently. For bcrypt that means choosing the cost factor empirically, often landing around twelve today. For argon2 it means setting memory cost first, since memory hardness is its main advantage, then adjusting iterations to hit your time target. Revisit these numbers periodically, because hardware keeps getting faster and safe parameters erode.
Verifying And Rehashing
Verification is straightforward because the stored hash is self-describing: you pass the candidate password and the stored hash to the library's compare function, and it extracts the algorithm, parameters, and salt from the stored string, recomputes, and tells you whether they match using a constant-time comparison internally. You never parse the hash yourself. The subtler part is migration. Over time you will raise your work factor, or you may decide to move from bcrypt to argon2. You cannot rehash a stored password without the plaintext, so the standard technique is opportunistic rehashing: when a user logs in successfully and you still hold their plaintext password in memory for that request, check whether the stored hash uses outdated parameters and, if so, recompute with the current settings and overwrite. Over a few weeks most active users transparently upgrade, and you can force a reset for the stragglers.
The Long Password Problem
Both algorithms have input-length considerations that bite teams who do not anticipate them. bcrypt ignores everything past seventy-two bytes, so a long passphrase silently has its tail discarded, which is fine for security but surprising when two long passwords sharing a prefix verify against each other. A common mitigation is to pre-hash the password with SHA-256 and base64-encode the result before feeding it to bcrypt, which compresses any length into a fixed short input, but you must do this consistently or existing hashes break. argon2 does not have this seventy-two-byte limit, which is one more reason teams handling passphrases lean toward it. Separately, never impose a low maximum password length on users in the name of these limits; allow long passwords and handle the encoding internally. And resist the temptation to disallow special characters, since arbitrary restrictions weaken passwords and frustrate the password managers your users should be relying on.
Operational Pitfalls
Several operational mistakes undermine otherwise correct hashing. Logging the password anywhere, even transiently in an error or a request dump, leaks the very secret you are protecting, so scrub credentials from logs and error trackers aggressively. Hashing on the client and treating the result as the password just means the hash becomes the password, so always hash server-side from the raw input over a secure channel. Do not roll your own scheme by chaining fast hashes with a homemade salt; you will reinvent known weaknesses. Choose a well-maintained, native-backed library rather than a pure-JavaScript reimplementation, because the native versions are both faster and less likely to have subtle bugs. Finally, hashing protects passwords at rest after a breach, but it does nothing against phishing, reused passwords, or session theft, so pair it with multi-factor authentication and breach-password screening. Strong hashing is necessary, not sufficient, for real account security.