Password storage is not where to be creative. The safe path is well established.

The first rule

Never store passwords with a plain fast hash (, -1, SHA-256) or with reversible encryption. A fast hash is exactly what brute force beats, and reversible encryption means a stolen key exposes every password.

Pick an algorithm

In order of preference: Argon2id as the default, then scrypt, then bcrypt, and PBKDF2 only where compliance forces it. All of them salt automatically or must be given a unique salt.

Set the parameters

Tune the work factor so a single hash takes a chosen wall-clock time on your hardware, then raise it as hardware improves. publishes current starting points, for example Argon2id memory and iteration settings and bcrypt cost factors.

Salt, and optionally pepper

Use a unique random salt per password; the libraries handle this. You may add a server-side secret, a pepper, kept out of the database, so a database-only leak is not enough.

Favor length over rules

Allow long passphrases, screen new passwords against known-breached lists as recommends, and avoid composition rules that hurt usability without adding entropy.

Migrating off a weak scheme

When moving away from a fast hash, either rehash each password on next login or wrap the old hash inside the new , and never keep the fast-hash version around.

The truncation nobody is told about

Most bcrypt implementations silently ignore everything past the 72nd byte of the input. A user who sets a 100-character passphrase is protected by its first 72 bytes, and neither they nor the application is told.

Nothing fails. Registration succeeds, login succeeds, the hash verifies. The security you believe you have and the security you have differ, quietly, forever.

Two consequences follow. Pre-hashing a long password before bcrypt (for example with SHA-256) removes the ceiling, but must be done carefully — a base64-encoded digest is safe, a raw binary one can contain a null byte and truncate even earlier. And the modern choice avoids the question entirely: Argon2 and scrypt take arbitrary-length input.

The general shape is worth keeping: a control that fails closed announces itself; a control that silently does less than you asked does not.