Home All Algorithms File Hash bcrypt Verify Hash Blog MD5 SHA-256 SHA-512 FAQ More Tools
Security

What Is a Salt in Password Hashing — and Why Every Password Needs One

📅 2026-05-12 ⏱ 6 min read ← Back to Blog

A salt is a random string mixed into a password before it's hashed. That's it. The reason salts exist is to make sure two users who happen to choose the same password end up with completely different hashes in the database.

Without salts, the password "password123" hashes to the same value for every user who chooses it — across every site on the internet that uses the same hash function. Attackers precompute that hash once and look for matches everywhere. With salts, that attack stops working entirely.

What a salt does

  • Makes two users with the same password get different stored hashes.
  • Defeats rainbow tables — precomputed lookups from hash → password.
  • Forces an attacker to crack each password individually instead of all at once.
  • Is stored in plaintext alongside the hash. It's not a secret.
  • Is generated randomly per password, typically 128 bits.

Why same passwords are a problem

Suppose 100 users on your site all chose "summer2024". Without a salt, each of their database records contains the same hash:

alice    e4b2c... (sha256 of summer2024)
bob      e4b2c... (sha256 of summer2024)
charlie  e4b2c... (sha256 of summer2024)
... 97 more rows with e4b2c...

An attacker who breaches the database doesn't need to crack each row. They just need to crack one. The moment they figure out that e4b2c... = summer2024, all 100 accounts fall instantly. Worse, if any other website also stores SHA-256 of summer2024 (and it does — that's an extremely common password), the attacker can match across breaches.

With a unique salt per user:

alice    salt=a1b2... hash=8f3d... (sha256 of a1b2...summer2024)
bob      salt=c9e4... hash=4a92... (sha256 of c9e4...summer2024)
charlie  salt=7f31... hash=ba0c... (sha256 of 7f31...summer2024)

Now each row is unique. Cracking Alice's hash tells you nothing about Bob's. The attacker must brute-force each password independently — multiplying their work by the number of users.

Salts kill rainbow tables

A rainbow table is a giant precomputed mapping from hashes back to passwords. Decades of leaked passwords have been hashed with MD5, SHA-1, and SHA-256 and stored in public databases. If you find a hash in one of these tables, you instantly know the original password.

For a rainbow table to work, the attacker needs to know the hashing scheme in advance. Plain MD5? Trivial. Plain SHA-256? Trivial. But MD5 of "3f7a92c8 + password"? The attacker would need to build a new table for that specific salt. With a unique random salt per user, the attacker would need to build 100 tables for 100 users — at which point it's no faster than just brute-forcing each password individually.

This is why salts are required by every modern password storage standard, including OWASP, NIST SP 800-63B, and PCI DSS.

How big should a salt be?

The salt needs to be large enough that two different users almost certainly get different salts. The standard is 128 bits (16 bytes) of cryptographically random data. With 2¹²⁸ possible salts, the chance of two users on the same site getting the same salt is essentially zero even with billions of users.

You generate the salt with a cryptographically secure random number generator — crypto.randomBytes(16) in Node.js, secrets.token_bytes(16) in Python, SecureRandom in Java. Never Math.random(), never the current timestamp, never username-based "salts".

Where the salt is stored

The salt is stored in plaintext right next to the hash. This is fine and intentional. The salt isn't supposed to be secret. Its only job is to make sure no two stored hashes are the same.

In modern password hash formats, the salt is embedded directly in the hash output:

bcrypt:   $2b$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
            ↑      ↑                       ↑
         cost   salt (22 chars)         hash (31 chars)

Argon2id: $argon2id$v=19$m=65536,t=3,p=1$c2FsdHNhbHQ$aGFzaGhhc2g
            ↑       ↑      ↑              ↑          ↑
         variant  ver    params         salt        hash

Verification reads the salt out of the hash string, re-runs the algorithm with the candidate password and that salt, and compares results. You never need to manage salts manually if you're using bcrypt, Argon2id, or scrypt correctly.

Pepper: an extra layer (optional)

A pepper is like a salt, but it's secret and the same for every user. Where the salt goes in the database next to the hash, the pepper lives somewhere else entirely — typically in an environment variable, a hardware security module, or a separate secrets store that isn't backed up to the same place as the database.

The idea: if an attacker steals your database but doesn't get the pepper, they can't crack any password offline — the pepper acts as an unknown constant they have to guess in addition to the password itself.

Peppers are optional and somewhat controversial. They add operational complexity (now you have a secret to rotate) and they don't help if the attacker compromises both the database and the application server. For most applications, a good salt + a strong slow hash (bcrypt or Argon2id) is sufficient.

Common salt mistakes

The good news: you usually don't have to think about this

If you're using bcrypt, Argon2id, scrypt, or PBKDF2 through their standard libraries, the salt is generated, stored, and used for verification automatically. You call bcrypt.hash(password, 12) and get back a complete $2b$12$... string with embedded salt. You call bcrypt.compare(password, hash) and verification just works. The library handles everything.

The mistakes happen when someone tries to be clever — reinventing salting on top of plain SHA-256, or building "their own" auth library, or storing salts in weird ways. Don't. Use the standard library. It's already correct.

Try generating a bcrypt hash with our bcrypt tool — every time you click Generate, you get a different hash for the same password because a fresh salt is auto-generated. That's what proper salting looks like in practice.

Our Network