---
url: https://findutils.com/guides/password-hasher
title: "bcrypt vs Argon2id: How to Hash a Password for Storage"
description: "Why MD5 and SHA-256 are wrong for passwords, what salt and cost do, how to read a $2b$ or $argon2id$ hash, and which settings OWASP gives."
category: security
content_type: guide
guide_type: subtopic
cluster: security
locale: en
read_time: 8
status: published
author: "olgunozoktas"
published_at: 2026-09-27T12:00:00Z
excerpt: "A password hash has to be slow and salted. Here is what bcrypt and Argon2id do that SHA-256 does not, how to read the encoded string, which settings to start from, and how to check a stored hash from PHP, Node or Python."
tag_ids: ["security", "passwords", "hashing", "bcrypt", "argon2"]
tags: ["Security", "Passwords", "Hashing", "bcrypt", "Argon2"]
primary_keyword: "bcrypt vs argon2id"
secondary_keywords: ["how to hash a password for storage", "bcrypt generator", "argon2id parameters", "bcrypt cost factor", "verify bcrypt hash", "bcrypt 72 byte limit"]
tool_tag: "password-hasher"
related_tool: "password-hasher"
related_tools: ["password-generator", "password-strength-checker", "password-breach-checker", "md5-hash-generator", "hmac-generator"]
og_image: "/images/content/guides/password-hasher-cover-20260927.webp"
image_alt: "A small brass key rests on the rim of a glass funnel above a mound of fine, uniform white grains, lit by a green accent light."
updated_at: "2026-09-27T12:00:00Z"
---

To store a password, hash it with a slow, salted password hash: Argon2id for a new system, or bcrypt where a framework or an existing database already uses it. Never store MD5 or SHA-256 of a password. The FindUtils [Password Hasher](/security/password-hasher/) makes bcrypt and Argon2id hashes and checks a password against a stored one. The hashing runs in a background thread in your browser and the password is not uploaded.

This guide covers why fast hashes fail, what the salt and cost settings do, how to read an encoded hash, which settings to start from, and how to check a hash made by another language.

## Why MD5 and SHA-256 Are Wrong for Passwords

MD5 and SHA-256 are designed to be fast. That is right for a file checksum and wrong for a password. When a user table leaks, the attacker guesses passwords offline and hashes each guess. The faster the hash, the more guesses per second, and a common password falls almost immediately.

A plain fast hash has a second problem: the same password always gives the same digest. `password` hashed with MD5 is always this:

```
5f4dcc3b5aa765d61d8327deb882cf99
```

Every user with that password has the same row value, and precomputed lookup tables already contain it. A password hash fixes both problems. It adds a random salt so that equal passwords give different hashes, and it makes each hash expensive to compute. The [Hash Generator](/security/md5-hash-generator/) is the right tool for checksums; the guide to [hashing text and files](/guides/how-to-hash-text-data-online/) explains what a checksum can and cannot prove.

## What the Salt and the Cost Factor Do

**The salt** is random bytes mixed into the hash. The Password Hasher draws a fresh 16-byte salt from the browser's cryptographic random generator for every hash, so hashing the same password twice gives two different strings. Both verify. The salt is not a secret and is written into the encoded hash, so there is no separate column to store.

**The cost** sets how much work one hash takes. The two algorithms measure it differently:

- **bcrypt** has one number, the cost factor. The work is 2^cost rounds, so each step up doubles the time. Cost 12 takes about twice as long as cost 11. The tool accepts 4 to 14 and defaults to 12.
- **Argon2id** has three: memory (in MiB), iterations (passes over that memory) and parallelism (lanes). Memory is what makes it "memory-hard": each guess needs that much RAM, which limits how many guesses a graphics card can run at once. The tool accepts 1 to 256 MiB, 1 to 10 iterations and parallelism 1 to 4, and defaults to 19 MiB, 2 and 1.

The Password Hasher shows how long each hash took. That time is your browser's time. A server can be faster or slower, so measure there before you settle a value.

## How to Read an Encoded Hash

An encoded hash is one string that carries the algorithm, the settings, the salt and the result. A verifier needs all of it, so store the whole string. Here is a bcrypt hash made by PHP:

```
$2y$05$4lbWs0pKtPq6kSCUSqnPrebM.P3.RGNvFMdAOv9lRBt/wYBHaxNKi
```

- `$2y$` is the bcrypt variant marker (`$2a$`, `$2b$` and `$2y$` are all common).
- `05` is the cost factor, always two digits. `05` means 2^5 = 32 rounds; a production hash usually reads `10` to `12`.
- The next 22 characters are the salt, and the last 31 are the hash itself. A bcrypt string is 60 characters in total.

And an Argon2id hash with the tool's default settings:

```
$argon2id$v=19$m=19456,t=2,p=1$stEqycYyx/yw8lZ/WZH7DA$Lm0DEtzpYVwwZ88xhpSqdGtfNqZdi34ujkn3BkaQDsQ
```

- `argon2id` is the variant. The verifier also reads `argon2i` and `argon2d`, but only makes Argon2id.
- `v=19` is Argon2 version 1.3 (0x13 in hex).
- `m=19456` is memory in KiB: 19456 / 1024 = 19 MiB. `t=2` is iterations and `p=1` is parallelism.
- The last two segments are the salt and the hash in Base64 without padding. The Password Hasher writes a 32-byte hash.

Paste either string into the Verify tab and the tool lists the same parts under "Detected".

## Which Settings to Use

The OWASP Password Storage Cheat Sheet is the usual reference here. For Argon2id it gives a minimum configuration of 19 MiB of memory, 2 iterations and parallelism 1, and it lists other combinations that trade more memory for fewer iterations. The Password Hasher's defaults are that minimum configuration. For systems that already use bcrypt, the same cheat sheet says to use a work factor of 10 or more and to keep the 72-byte password limit in mind. It also covers scrypt, and PBKDF2 where FIPS-140 compliance is required, which this tool does not make.

The cheat sheet's values are minimums, not targets. A practical approach:

1. Start from the minimum for your algorithm.
2. Raise the cost on your login server until one hash takes as long as you can accept at peak load.
3. Do not go below the minimum to make logins faster; add capacity instead.

Parallelism above 1 only helps if the server hashing the password has cores to spare during a login.

## bcrypt's 72-Byte Limit

bcrypt uses at most the first 72 bytes of a password and ignores the rest without an error. Two long passphrases that share their first 72 bytes produce hashes that verify each other. Bytes are not characters: an `é` takes 2 bytes in UTF-8, and many emoji take 4, so a 40-character password can already pass the limit.

The Password Hasher refuses a bcrypt password over 72 bytes instead of producing a hash that silently ignores part of it, and it shows the byte count as you type. If your users may choose long passphrases, use Argon2id, which has no such limit.

## Verifying a Stored Hash from PHP, Node or Python

The encoded formats are shared across languages, so a hash made on the Password Hasher verifies in your app and the other way round. In your own code the check is one call:

```php
// PHP: bcrypt ($2y$) and Argon2id
password_verify($password, $storedHash);
```

```js
// Node, with the bcrypt or argon2 package
await bcrypt.compare(password, storedHash);
await argon2.verify(storedHash, password);
```

```python
# Python, with bcrypt or argon2-cffi
bcrypt.checkpw(password.encode(), stored_hash.encode())
argon2.PasswordHasher().verify(stored_hash, password)
```

About the bcrypt prefixes: PHP writes `$2y$`, most other current libraries write `$2b$`, and older ones wrote `$2a$`. The markers record fixes to older implementations, not a different algorithm. `$2b$`, for example, fixed a length bug that only affected passwords longer than 255 bytes, which bcrypt could never use in full anyway. For ordinary passwords the three give the same result. The Password Hasher treats `$2y$` and `$2a$` as `$2b$` when it verifies, and it writes new bcrypt hashes as `$2b$`. If a library of yours rejects `$2y$`, replacing the prefix with `$2b$` is the usual fix.

When a login fails and you do not know why, paste the value from your users table and the expected password into the Verify tab. "No match" means the stored value is wrong, for example truncated by a column that is shorter than 60 characters. "This is not a bcrypt or Argon2 hash" usually means the column holds a 32- or 64-character hex string, which is MD5 or SHA-256, not a password hash.

## Why There Is No API for This Tool

Many FindUtils tools can also be called over REST or MCP. The Password Hasher cannot, by design. bcrypt and Argon2id are meant to be slow and memory-hungry, and that work belongs on your own device or your own server rather than on a shared service. It also keeps production passwords off anyone else's network. The page itself loads the usual analytics and ads, but the password and the hash stay in the page and are not saved to browser storage. For a real production password, the safest place to hash it is still the server that stores it.

## Related Tools

- [Password Generator](/security/password-generator/): make a random password to hash for a seed user.
- [Password Strength Checker](/security/password-strength-checker/): see how a password scores before you store it.
- [Password Breach Checker](/security/password-breach-checker/): check whether a password appeared in a known breach. A slow hash does not protect a password that is already on a list.
- [HMAC Generator](/security/hmac-generator/): sign a message with a secret key. That is a different job from password storage.
- [MD5 Hash Generator](/security/md5-hash-generator/): fast checksums for files and text.

## FAQ

**Is Argon2id always better than bcrypt?**
For a new system, the OWASP Password Storage Cheat Sheet lists Argon2id first. bcrypt with a cost of 10 or more is still acceptable where a framework already uses it, as long as passwords stay within 72 bytes.

**Can I raise the cost of existing hashes?**
Not directly, because the original password is needed. Most frameworks re-hash at the next successful login when the stored cost is lower than the current setting; PHP's `password_needs_rehash` is one example.

**Do I need a pepper as well?**
A pepper is a secret added to every password and stored outside the database. It is optional extra protection and this tool does not add one. If you use a pepper, a hash made here will not verify in your app.
