Precomputed tables and rainbow tables work because an unsalted hash of a given input is always the same value. Salting removes exactly that assumption.
What goes wrong without a salt
If passwords are hashed raw, hash("password") is the same everywhere. One precomputed table cracks that account on every site at once, and two users who chose the same password have the same stored hash, which is visible in a database leak.
What a salt is
A salt is a per-password random value, typically 16 bytes or more, generated when the password is set and stored in the clear next to the hash. The stored record is effectively hash(salt + password) together with the salt itself.
Why tables stop working
To use a precomputed table against a salted hash, the attacker would have to have built the table using this specific salt. With a unique salt per row, no table can be reused across accounts, and precomputation buys nothing: the attacker is forced back to computing hashes for one target at a time.
What salting does not do
Salting does not slow down an attack against a single known salt. Once the attacker has the salt (it is not secret), they can still guess-and-check against that one target as fast as the hash allows. Defeating that is the job of a slow (key derivation function), not the salt.
A salt is not a secret and is not a substitute for a slow password hash. Correct password storage needs both: a unique salt to kill precomputation, and a deliberately slow function to make each guess expensive.
The salt is not a secret, and treating it as one causes real bugs
A salt is stored in plain sight, usually inside the hash string itself, and that is correct. Its job is uniqueness, not concealment — it makes one precomputed table useless against many accounts, and it does that whether or not the attacker can read it.
The failure this misunderstanding produces is specific: teams that treat the salt as secret sometimes make it shared — one application-wide value, kept in config — reasoning that a hidden salt is stronger than a visible one. A shared salt is barely a salt at all. Every user with the same password gets the same hash again, and a table built once against that one salt cracks the whole database.
Per-user, random, stored openly. The thing that must be secret is the pepper, if you use one, and that is a different mechanism with different handling.