UNDERSTAND. CHOOSE. ENJOY.LESS NOISE. BETTER CHOICES.
Development/Practical guide

WordPress password hashing: bcrypt, legacy hashes and migration

Understand WordPress bcrypt hashes, compatibility with older accounts and the checks to make before changing a login integration.

THE USEFUL ANSWER

Since WordPress 6.8, user passwords use bcrypt by default with SHA-384 pre-hashing. Older formats remain verifiable. Use the WordPress password functions instead of reproducing the storage algorithm in your own application.

  • Hashing is not reversible encryption.
  • An old hash in the database does not automatically mean an upgrade failed.
  • Test authentication integrations with both old and newly created accounts.

What password algorithm does WordPress use?

WordPress 6.8 changed the default user-password mechanism from portable phpass hashing to bcrypt. A SHA-384 preprocessing step avoids simply truncating a long password at bcrypt’s input limit. WordPress marks its format separately; the normal default prefix is $wp$2y$.

This describes core behaviour, not every customised installation. Plugins can replace or filter password handling. Application passwords and recovery keys follow different mechanisms, so “WordPress uses bcrypt for every secret” would be an inaccurate shortcut.

Why do MD5 and phpass still appear?

WordPress retains verification paths for legacy formats. A successful login after the upgrade, or a password change, allows the normal account flow to store a hash using the current mechanism. The hashing change alone does not require resetting every account.

Old instructions to edit class-phpass.php are not a sensible modern migration plan. Core edits create a fragile maintenance dependency. Hashing an existing hash again also does not recreate the original user password.

ObservationCareful interpretationRecommended action
$wp$2y$ prefixDefault WordPress bcrypt formatVerify through WordPress APIs
$P$ prefixPotential legacy phpass formatRetain compatibility during migration
Short MD5-like valuePotential older stored formatDo not choose it for new passwords

Use the platform boundary, not a copied algorithm

wp_hash_password() creates a hash and wp_check_password() checks a secret against an existing value. Calling password_verify() directly on a $wp… value can fail because WordPress preprocessing and its prefix are part of the format. Comparing two freshly generated hashes is not a password-verification method either.

A password check is also not a complete login implementation. Sessions, permissions, plugins, additional authentication factors and abuse controls remain separate responsibilities. For an external application, start with a supported authentication integration rather than exposing the users table.

A practical migration check

Use a staging copy with unnecessary real data removed. Prepare one account from before the upgrade and another created afterwards. Check a successful login, an incorrect password, account recovery and the application that consumes authentication. Confirm that neither passwords nor full stored hashes appear in logs.

Keep a backup and a rollback plan before changing authentication. When a plugin supplies its own hashing behaviour, test and document that case separately; passing a core-only test does not validate the plugin. If the wider application architecture is still undecided, use our WordPress, Django and Rails comparison to separate publishing needs from custom workflows.

One more question?

Must every password be reset after WordPress 6.8?

Not because of this change alone. WordPress retains compatibility with older hashes. A separate security incident may still justify a controlled reset.

Can a hash be decrypted to recover a password?

Password hashing is not designed to be reversible. Use a controlled reset process when a user forgets their password.

Does bcrypt make a WordPress site secure by itself?

No. Updates, access control, backups, session security and protection against abusive login attempts still matter.