Free · runs in your browser

Django Password Hash Generator

Django Password Hash Generator. Enter a password and iteration count to instantly generate a ready-to-use Django password hash. If you want to generate an sha1 hash, a free tool is ready to use right here, with no account or install.

Your values

Django can store and verify all four hashers. PBKDF2 needs no extra package; Argon2 needs argon2-cffi and bcrypt needs bcrypt on your server.

Hashed in your browser only, as UTF-8. Use a sample password, not a real credential.

Leave blank for a new random salt each time, like Django. Paste one for a repeatable hash. It cannot contain $. For bcrypt, use a 22-character bcrypt salt.

Used only when the salt is blank. Django uses 22 letters and digits (about 128 bits). bcrypt always uses its own 16-byte salt.

SHA-512 is not built into Django; it needs a PBKDF2PasswordHasher subclass that sets algorithm and digest.

Django 6.1 uses 1,500,000 (5.2 used 1,000,000; 5.0 used 720,000). OWASP's floor for PBKDF2-HMAC-SHA256 is 600,000. Higher counts take longer in your browser.

Result

Encoded password hash / verification result
—
PASSWORD_HASHERS entry
—
Algorithm
—
Parameters (work factor)
—
Salt
—
Hash (digest)
—
Record length
—
Computation time
—
  • No account, no logging
  • Open formulas explained below
  • Works offline once loaded

Need to paste a valid hash into a database row, a fixture or a test? This Django Password Hash Generator turns any plain text into the encoded string Django stores in the password column, with the algorithm name, iterations, salt and digest in the order Django expects. Everything you read below explains what each part of that string means, so you can trust what you paste and verify it later.

What a Django password hash generator produces

A password hash generator takes a password, adds a random salt and runs a one-way hashing function many times. What comes out is never the password itself, only a fixed-length digest you can compare against later. Django wraps that digest in a self-describing record so one table can hold hashes made by different hashers over the years. The Django password hash you generate here follows the same layout as one created by make_password() inside django.contrib.auth. The sha512 hash generator is completely free to use, with no rate limits, watermarks, or hidden restrictions.

Because the record names its own algorithm, Django never has to guess how to check a login. It reads the prefix, finds the matching hasher in the project settings and recomputes the digest. If the recomputed value equals the stored one, the user is in.

The anatomy of the encoded string

Every password hash in Django is a single line with dollar signs between its parts. For PBKDF2 the layout is:

<algorithm>$<iterations>$<salt>$<hash>

Here is a real record built from the sample password Maple-Ledger-5081, the salt Tw3nKq8LzR5v and 1,000,000 rounds of PBKDF2 with SHA256:

pbkdf2_sha256$1000000$Tw3nKq8LzR5v$8OetQt/GHssao9IIXUNh2se+l0IL77qw1wxUinXeW8c=
pbkdf2_sha256
The hashing algorithm, which also tells Django which hasher class to call.
1000000
The iterations, also called the work factor: how many times the function is run over the input.
Tw3nKq8LzR5v
The random salt that makes two identical passwords produce different hashes.
8OetQt/GHssao9IIXUNh2se+l0IL77qw1wxUinXeW8c=
The resulting digest, Base64 encoded, 44 characters for a 32-byte SHA256 output.

How to use this Django password hasher

The page is built around one job: give it a password and settings, get back a record Django accepts. Follow the steps in order. Use the free htpasswd file generator to get a result in seconds — right in your browser.

  1. Type the plain-text password you want to hash. Use a throwaway sample, never a real credential.
  2. Choose the hashing algorithm: PBKDF2, Argon2, bcrypt or scrypt.
  3. Leave the salt on random, or paste your own when you need a repeatable result for a test.
  4. Set the iterations (or cost factor) for the algorithm you picked.
  5. Press the generate button, then copy the encoded string into your fixture, SQL statement or shell session.

Hashing happens in your browser, so the password you type is not sent anywhere. That is also why you should treat this page as a debugging aid and keep production secrets out of any online tool.

Password hashing algorithms Django supports

Django ships several hashers, each wrapping one hashing algorithm. The table lists what a generated record starts with and what each choice costs an attacker.

Password hashing algorithms Django supports
AlgorithmDjango hasher classRecord prefixMemory-hard
PBKDF2 with SHA256PBKDF2PasswordHasherpbkdf2_sha256No
PBKDF2 with SHA1PBKDF2SHA1PasswordHasherpbkdf2_sha1No
Argon2idArgon2PasswordHasherargon2Yes
bcrypt with SHA256BCryptSHA256PasswordHasherbcrypt_sha256No
scryptScryptPasswordHasherscryptYes

PBKDF2 as the default algorithm

Django uses PBKDF2 unless you change it. It is a password stretching and key derivation method recommended by NIST, it needs no third-party package and it is accepted where FIPS compliance matters. Its weakness is that it needs very little memory, so an attacker with GPUs can run huge numbers of guesses in parallel.

Argon2 and the memory-hard family

Argon2 won the Password Hashing Competition, and the Django default variant is Argon2id. It is deliberately memory-hard: every guess needs a block of RAM, which blunts GPU and ASIC speedups. Django does not make it the default only because it depends on the argon2-cffi package. Install it with python -m pip install django[argon2].

bcrypt and the cost factor

bcrypt is a long-lived algorithm built for password storage. Its single dial is the cost factor, where each increase of 1 doubles the work. Django uses the SHA256-prehashed variant, which avoids bcrypt's 72-byte input limit and the null-byte problem. Cost 12 is a common floor for new systems.

scrypt and its work factor

scrypt also eats memory on purpose, which limits the parallel attacks that make PBKDF2 and bcrypt cheaper to crack at scale. Django exposes its work factor as a power of two. It needs OpenSSL 1.1 or newer on the server.

Salt, iterations and other password hashing settings

The generator exposes the same dials Django does. Understanding them tells you what to set.

Salt entropy and salt length

The salt defeats a rainbow table, a precomputed list of hashes for common passwords. Django's own hashers create a random salt of 22 characters from a 62-character alphabet, which works out to roughly 131 bits of salt entropy. A salt length of 16 bytes or more is the usual guidance. A salt is stored in the open next to the digest, so it is not a secret and a hashed value still verifies without any extra key.

Iterations and the cost of a guess

The iterations field you fill in becomes the second segment of the hash the generator outputs, and it sets how much a single guess costs. Verification time grows in a straight line with that number, so use it to pick the value for your record:

$$T_{verify} = N_{iterations} \times t_{round}$$

An attacker's throughput is the inverse, so doubling the iterations halves the guesses they can make per second on identical hardware:

$$R_{guesses} = \frac{1}{T_{verify}}$$

For the generated record above, one verification of 1,000,000 rounds took 0.212 seconds in a Python test, so a single core manages about 4.7 guesses per second, not hundreds of thousands.

Pepper and why it is optional

A pepper is a secret added to every password before hashing and stored outside the database, for example in an environment variable. It helps if only the database leaks, but it complicates rotation and Django does not provide one by default. If you use one, combine it with the password before typing it into this generator; the stored encoded hash never reveals it.

Why your hashing function choice matters

Hashing is a defence against one scenario: someone obtains the database and tries to turn stored hashes back into passwords. A one-way function cannot be reversed, so they have to guess, hash each guess with the stored salt and compare. Your password storage choice sets the price of every guess.

Brute-force and dictionary attack costs

A published experiment against Django records used hashcat on rented GPUs with the well-known rockyou wordlist. A dictionary attack like that recovered roughly a tenth to a quarter of the hashes in real data sets within hours, even though the passwords were hashed. Pure brute-force or brute forcing every combination is far slower, but weak passwords fall to word lists first. A leaked encoded hash in the format shown earlier is exactly what an attacker feeds to hashcat. The lesson: slower hashing helps, and so does a decent password policy.

Hashing is not encryption

People often search for how to decrypt a Django hash. You cannot, because there is no key. Encryption is reversible and a hash is not. If you ever see a guide describing how to encrypt a password for login, it means to hash it. The only recovery path is guessing, which is why the iteration count matters so much. The output of this tool is a one-way digest: the only check is to hash a guess with the same salt and compare.

How Django verifies a password against a hash

When a user signs in, Django checks a login with check_password(password, encoded). It splits the stored string on the dollar signs, loads the hasher named by the first part, recomputes the digest with the stored salt and iterations, and compares in constant time. You can build a record yourself with make_password(), and a value that cannot ever match starts with an exclamation mark, which Django treats as an unusable password.

You can also confirm a generated hash in a shell:

from django.contrib.auth.hashers import check_password
check_password("Maple-Ledger-5081", "pbkdf2_sha256$1000000$Tw3nKq8LzR5v$8OetQt/GHssao9IIXUNh2se+l0IL77qw1wxUinXeW8c=")

It returns True when the stored password matches. The answer is False for any other plain-text password, and a ValueError appears when the prefix names a hasher missing from your list.

Testing a legacy password hash with this Django password hasher

Priya Nair inherits a membership site whose oldest user rows carry pbkdf2_sha256$720000$ prefixes, and she wants to know how much slower a stronger record would be before she changes anything. She opens the Django password hasher, types the staging password Cedar-Tally-9026 and pastes the salt Zp4mH7cWbN2x from a test account so the output is repeatable.

First she enters 720,000 iterations with PBKDF2 and SHA256, the value in those rows, and gets:

pbkdf2_sha256$720000$Zp4mH7cWbN2x$fD6tkrR/o6hOV3uffpKhhBWtXTfqvtPFOqYvBYABFZQ=

Her staging server verifies it in 154 ms. That clears OWASP's floor of 600,000 iterations for PBKDF2-HMAC-SHA256, but it sits below the 250 ms she wants for a login. Next she changes only the iterations field to 1,200,000 and generates a second hash:

pbkdf2_sha256$1200000$Zp4mH7cWbN2x$pngExi/ebOGjRC10kEmm+i7/ZfVzAYXo9TpYzIuRcmo=

The new value is 1,200,000 ÷ 720,000 = 1.67 times the work, and the staging server now takes 240 ms. The two digests are completely different even though only one input changed, which confirms the iterations are baked into the result.

  • She runs check_password() against both records in a shell and gets True for each, so the old hash keeps working.
  • Scaling linearly, 240 ms × 1,300,000 ÷ 1,200,000 predicts about 260 ms, just inside her 250 ms floor.
  • She sets the next test to 1,300,000 iterations and plans to subclass the hasher with that value, so Django rehashes each account at its next login.

The whole check takes minutes, and the hash she pastes into the fixture is one she has already seen verify.

Configure PASSWORD_HASHERS in Django settings

Django chooses the algorithm through the PASSWORD_HASHERS setting. It stores new passwords with the first entry and verifies old ones with whichever entry matches the stored prefix.

PASSWORD_HASHERS = [
    "django.contrib.auth.hashers.Argon2PasswordHasher",
    "django.contrib.auth.hashers.PBKDF2PasswordHasher",
    "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
    "django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
    "django.contrib.auth.hashers.ScryptPasswordHasher",
]

Keep older entries in the list so that existing users can still log in. This default list is the reason a generated hash from any supported algorithm will verify in a stock project.

  • Put your preferred hasher first in PASSWORD_HASHERS; the other entries remain for checking.
  • Install the bcrypt or argon2-cffi package before listing those hashers.
  • Leave PBKDF2 in the list while any user row still carries a pbkdf2_sha256 prefix.
  • The hashers and make_password() come from django.contrib.auth.hashers, so an encoded password string verifies there.

Password upgrading and work factor changes in Django

Django upgrades hashes silently. When a user logs in with a correct password and the stored record uses a different algorithm or fewer iterations than the first hasher, Django rehashes and saves the stronger version. This password upgrading needs no reset email.

Increasing the work factor over time

Subclass a built-in hasher and raise its iterations to bump the work factor. Release notes show Django has increased its PBKDF2 default several times since 2012, so a hash created years ago is probably below today's default. Inactive accounts never log in, so they never upgrade. For those you can wrap the old hash inside a stronger one, a technique often called layering.

Increasing the salt entropy

The same subclassing approach lets you lengthen the salt by overriding salt(). The default is already sufficient for nearly every project, and this change only affects hashes made afterward. A longer custom salt shows up in the salt segment of the encoded string.

Password validation before you hash a sample

A strong hash cannot save a weak password. Django's password validation runs before hashing, through AUTH_PASSWORD_VALIDATORS, so test samples in the generator should meet the same length and common-password rules your real accounts face. Hashing passwords never replaces choosing good passwords, and every user who picks a weak one weakens the whole table. Run a sample through the validators first, then hash it, so the passwords in your fixtures behave like what each user will actually type.

Common reasons a generated password hash fails

When a record pasted into the database will not verify, the cause is almost always one of these:

  • A missing or misspelled prefix, so no hasher matches and Django raises ValueError.
  • Whitespace or a line break copied along with the encoded string.
  • Hex used where Base64 was expected for the digest.
  • A hasher that is not present in PASSWORD_HASHERS.
  • A third-party library such as bcrypt not installed in that environment.
  • A framework mismatch: a bcrypt value generated for another stack uses $2b$ and lacks the bcrypt_sha256$ wrapper Django expects.
  • A value truncated by a column that is too short, since Django's password column holds 128 characters.

Security checklist for Django password storage

  • Serve login pages over HTTPS so a hash is never created from a password exposed by password sniffing.
  • Anonymize database dumps before giving them to developers or staging servers.
  • Rate-limit login views and keep the admin off the open internet so a stolen hash cannot be paired with a live login.
  • Benchmark verification time and aim for roughly 250 to 500 milliseconds on production hardware, because very high latency turns the login page into a target.
  • Follow NIST and OWASP guidance for iteration counts, which currently call for at least 600,000 rounds of PBKDF2 with SHA256.
  • Keep every legacy hash format until it has been upgraded.

Keeping every backward-compatible hash format working matters as much as the new algorithm, and compatibility with FIPS rules is a reason to keep PBKDF2.

Choosing a stronger password hashing function for your users

Picture a copied password column: the attacker sees each algorithm prefix and learns which records are cheapest to compute. Every record still using a weak setting is vulnerable, and reused credentials put users at risk elsewhere too. The best protection is a modern password hashing function chosen before a breach. Argon2id is what most security reviewers now recommend for new projects, and Django lets you adopt Argon2id with one settings change.

After a leak, move to a slower hasher so new password hashes cost more to attack, and prompt a password reset for inactive accounts. This tool helps: generate a sample, compare it with what is stored and verify password handling in a staging copy first. The dollar-sign separators make each record easy to split by script when you audit how many users still carry an old prefix, and the same audit covers the passwords of every account in the table. Authentication stays safe only while those prefixes are current.

Frequently confused terms

The record has four segments, and these terms describe them. A hash version field is not needed, because the prefix already carries it. Time cost is how long one hash takes, parallelism is how many lanes Argon2 or scrypt run side by side, and rounds is bcrypt's word for iterations. A custom hasher is a subclass you write, and an encoded hash is the full stored record. Parallel attacks are what memory-hard designs try to slow, and the auth_user table is where the legacy user model keeps its password column.

Cryptographically secure randomness creates the salt segment, a GPU is the attacker's cheap guessing engine, and a benchmark on your own hardware beats any chart for choosing an iteration count. Repeating the function that many times is called stretching. SHA1 hashers exist only to verify old data, FIPS is a government standard, and memory is the RAM each guess needs.

Frequently Asked Questions

What does the hash format mean?
It's four parts separated by $: the hasher name (pbkdf2_sha256), the iteration count used, the random salt, and the resulting derived key encoded in Base64 -- this is exactly the string Django stores in a user's password field, and exactly what Django's own check_password() function parses when verifying a login.
Will this exactly match what Django itself generates?
The algorithm and format are identical to Django's PBKDF2PasswordHasher -- PBKDF2-HMAC-SHA256 with a 12-character alphanumeric salt and Base64-encoded output. Set the iteration count to match your specific Django version's default (it increases periodically as Django's maintainers keep pace with hardware) for an exact match, since Django only requires the iteration count stored in the hash to be at least the configured minimum, not an exact value.
Why does the iteration count vary by Django version?
Django's maintainers periodically raise the default PBKDF2 iteration count in new releases to keep pace with faster hardware -- a fixed default from several years ago would be considered too weak today. Check your target Django version's PASSWORD_HASHERS documentation, or the actual value Django's own make_password() produces in your environment, for the exact current default.
Can I use this to manually set a user's password in a Django database?
Yes -- the generated string can be inserted directly into a Django User model's password field (e.g. via a raw SQL UPDATE or a data migration), and Django's authentication system will accept it exactly as if make_password() had generated it, since the format is identical.
Is my password sent anywhere?
No. The entire hash is computed locally using the Web Crypto API -- your password and the derived hash are never transmitted to a server or stored.

About the author

Vinitha Pu

Founder and designer

Vinitha Pu is a graphic designer based in Palakkad, Kerala, who builds free, privacy-friendly browser tools. She studied Textile and Apparel Design at Avinashilingam University and completed a C-Tech in Graphic Design at Image Creative Education in 2025. She designs, builds and maintains the password generators, strength checkers, hash tools and security calculators on PasswordHive.

  • Content author: Vinitha Pu
  • Fact checked by: Vinitha Pu. Each tool's output is checked against official test vectors or a reference implementation, and every strength and crack-time figure against the formula it uses, before it goes live.
  • Published Updated