Compatibility guide
The WordPress 6.8 password hashing change, explained properly.
WordPress replaced its twenty-year-old phpass hasher with bcrypt. Here is what actually changed, what stayed compatible, and the short list of situations where you need to act.
Since 6.8, wp_hash_password() produces $wp$2y$ values instead of phpass $P$ hashes.
Old hashes keep verifying, and each account is upgraded silently on its next successful login.
Only manual database edits, downgrades, and code that reads user_pass directly need attention.
What changed under the hood
From WordPress's earliest days until 6.7, passwords were hashed with the portable phpass algorithm: 8,192 rounds of salted MD5, stored with a $P$ prefix. It was a reasonable design in 2004 and an outdated one for years before it was replaced — MD5-based stretching is far cheaper for an attacker to brute-force than modern password hashes on the same hardware.
WordPress 6.8 (April 2025) moved the default to bcrypt via PHP's native password_hash(). Core adds one preparation step of its own: the password is first run through HMAC-SHA-384 (keyed with the constant string wp-sha384) and Base64-encoded, and bcrypt hashes that fixed-length material. This sidesteps bcrypt's 72-byte truncation so long passphrases retain their full entropy. The stored result is the bcrypt string with a $wp marker in front — the $wp$2y$ format covered in detail in the companion guide.
Alongside user passwords, 6.8 also stopped using phpass for the faster, non-password cases — application passwords, password-reset keys, and personal-data-request keys now use a dedicated fast hash instead of an expensive password hasher they never needed.
Why your upgrade was uneventful
The change was engineered to be invisible. wp_check_password() still recognizes every historical format — $wp$2y$, phpass $P$, plain bcrypt, even ancient raw MD5 — so no stored hash stopped working when 6.8 landed. When a user with an old-format hash signs in successfully, WordPress takes the opportunity to rehash the password it just verified and writes the new $wp$2y$ value.
The practical consequence: your wp_users table converts itself gradually, one login at a time. Dormant accounts keep their $P$ hashes indefinitely, and that is fine. There is no batch-conversion step to run, and no plugin needed to "finish" the migration.
The situations that do need attention
Manual password edits. Tutorials that say "set user_pass to an MD5 of the new password" still technically work as an emergency reset — WordPress accepts MD5 and immediately rehashes on login — but if you are setting a hash by hand on a 6.8+ site, generate a proper $wp$2y$ value with the modern generator instead. Do not generate plain bcrypt and prepend $wp yourself: without the SHA-384 step the value will never verify.
Downgrades. WordPress 6.7 and earlier do not understand the $wp prefix. If you roll a site back, every account rehashed since the upgrade is locked out until its password is reset. Check what fraction of your users table already carries $wp$2y$ before planning a rollback.
Code that reads user_pass. Integrations that copy WordPress hashes into another system, or verify them with a standalone phpass library, will meet values they cannot check. Anything consuming hashes needs to implement the WordPress sequence or, better, stop sharing password hashes between systems at all.
Plugins that replace hashing. Sites already using a plugin that switched WordPress to bcrypt or Argon2 before 6.8 should review whether it is still needed — Core also exposes a filter for choosing another supported algorithm, and two layers doing the same job is where login bugs come from.
A five-minute audit for your own site
- Confirm the WordPress version; 6.8 or later means bcrypt is already your default.
- Sample a few rows of
user_pass— a mix of$wp$2y$and$P$is healthy. (Unsure which table? See finding the users table.) - Search your custom code and integrations for direct reads of
user_passor calls to standalone phpass libraries. - If a security plugin advertises "bcrypt password hashing" as a feature, evaluate whether it still earns its place.
- Leave dormant accounts' old hashes alone — or force a reset if your policy requires retiring MD5-era credentials.
Quick answers
Do I need to do anything after upgrading to WordPress 6.8?
Usually nothing. Existing hashes keep working and are upgraded automatically as users log in. Action is only needed if you edit hashes manually, plan a downgrade, or run code that reads password hashes directly.
Did WordPress 6.8 log everyone out or force password resets?
No. Verification still accepts the old formats, so sessions and passwords continued to work through the upgrade. The rehash to the new format happens quietly at each user's next login.
Is WordPress bcrypt the same as PHP's password_hash()?
Almost. Core uses PHP's bcrypt, but hashes an HMAC-SHA-384 of the password rather than the password itself, and stores the result with a $wp prefix. Plain password_verify() against the raw password will therefore fail.
Can I still create phpass $P$ hashes for an old site?
Yes. Installations on WordPress 6.7 or earlier still expect phpass, and a local generator can produce a portable $P$ hash for a legacy-compatible reset when you genuinely need one.
Primary references: wp_hash_password(), wp_check_password(), and the Make WordPress announcement.