2026年11月12日

2026年11月12日

WordPress Password Strength Policy: Enforce a Minimum Length in PHP

This article is a localized edition of the Japanese original. Read the Japanese original

The WordPress admin shows a password strength meter, but if you tick "Confirm use of weak password," it saves even a short password. If you want to enforce a minimum length or similar rules on a membership site or a site run by several people, check passwords on the server side (in PHP) and reject those that do not meet the rules. The hook behavior in this article was checked against the WordPress source code (wp-admin/includes/user.php and wp-login.php) as of November 2026.

Choosing the requirements

Rules such as "must contain upper case, lower case, digits and symbols" used to be common, but the US National Institute of Standards and Technology (NIST) digital identity guideline SP 800-63B (revision 4, published 2025) takes a different approach:

  • Require at least 15 characters when the password is the only factor, or at least 8 characters when it is combined with multi-factor authentication
  • Accept passwords of 64 characters or more
  • Do not impose composition rules (requirements to mix character types)
  • Compare passwords against a list of commonly used or known-breached passwords (a blocklist) and reject matches
  • Do not require periodic changes (force a change when there is evidence of compromise)

Requiring character types is known to push people toward predictable patterns like "Password1!". The code in this article is built around a minimum length, not containing the username or email address, and rejecting common passwords, and lets you add character-type checks if an internal policy requires them.

Where WordPress sets passwords

Screen Hook used for the check
Changing the password on the Profile or Edit User screen user_profile_update_errors
Creating a user under Users → Add New User user_profile_update_errors (the $update argument is false for new users)
Resetting via "Lost your password?" on the login screen validate_password_reset

The standard WordPress registration screen (wp-login.php?action=register) has no password field. After registering, the user receives a link by email and sets the password on the reset screen in the table above. So you do not need to check passwords with the registration_errors filter for the standard registration screen, and checking $_POST['pass1'] there would actually block registration.

Create the checking plugin

Save the file as password-policy.php in the wp-content/mu-plugins/ folder (create it if it does not exist), and it loads without needing activation. You can also put it in wp-content/plugins/ as a regular plugin and activate it. Avoid the theme's functions.php, because the check would disappear when you switch themes.

<?php
/**
 * Plugin Name: Site Password Policy
 * Description: Rejects weak passwords in the admin and on password reset.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

const SITE_PW_MIN_LENGTH = 15; // 8-12 is acceptable if two-factor authentication is required

/**
 * Returns the password's problems as an array of messages (empty if none).
 */
function site_pw_problems( $password, $login = '', $email = '' ) {
	$problems = array();

	if ( mb_strlen( $password, 'UTF-8' ) < SITE_PW_MIN_LENGTH ) {
		$problems[] = sprintf( 'Your password must be at least %d characters long.', SITE_PW_MIN_LENGTH );
	}

	$lower = strtolower( $password );
	foreach ( array( $login, strstr( (string) $email, '@', true ) ) as $part ) {
		if ( is_string( $part ) && strlen( $part ) >= 3 && false !== strpos( $lower, strtolower( $part ) ) ) {
			$problems[] = 'Your password must not contain your username or email address.';
			break;
		}
	}

	// Examples of common passwords. Matters if you lower the minimum length. In production, extend it with a list of breached passwords
	$blocklist = array( 'password', 'password123', 'passw0rd', '123456789', '1234567890', 'qwertyuiop', 'iloveyou', 'wordpress', 'administrator' );
	if ( in_array( $lower, $blocklist, true ) ) {
		$problems[] = 'Commonly used passwords are not allowed.';
	}

	// Enable the next two checks only if your policy requires character types
	// if ( ! preg_match( '/[A-Z]/', $password ) || ! preg_match( '/[a-z]/', $password ) ) {
	// 	$problems[] = 'Your password must contain upper and lower case letters.';
	// }
	// if ( ! preg_match( '/[0-9]/', $password ) ) {
	// 	$problems[] = 'Your password must contain a digit.';
	// }

	return $problems;
}

/**
 * Profile changes, user edits and Add New User.
 */
add_action( 'user_profile_update_errors', function ( $errors, $update, $user ) {
	if ( empty( $_POST['pass1'] ) ) {
		return; // Update without a password change
	}
	$password = trim( wp_unslash( $_POST['pass1'] ) );

	$login = isset( $user->user_login ) ? $user->user_login : '';
	$email = isset( $user->user_email ) ? $user->user_email : '';
	if ( $update && ! empty( $user->ID ) ) {
		$current = get_userdata( $user->ID );
		if ( $current ) {
			$login = $current->user_login;
		}
	}

	foreach ( site_pw_problems( $password, $login, $email ) as $i => $message ) {
		$errors->add( 'site_pw_' . $i, '<strong>Error:</strong> ' . esc_html( $message ), array( 'form-field' => 'pass1' ) );
	}
}, 10, 3 );

/**
 * Password reset from the login screen.
 */
add_action( 'validate_password_reset', function ( $errors, $user ) {
	if ( empty( $_POST['pass1'] ) || ! ( $user instanceof WP_User ) ) {
		return;
	}
	$password = trim( wp_unslash( $_POST['pass1'] ) );

	foreach ( site_pw_problems( $password, $user->user_login, $user->user_email ) as $i => $message ) {
		$errors->add( 'site_pw_' . $i, '<strong>Error:</strong> ' . esc_html( $message ) );
	}
}, 10, 2 );

Key points in the code:

  • WordPress adds backslashes (\) before quotes and similar characters in $_POST values, so restore the original string with wp_unslash() before checking. WordPress core also strips leading and trailing whitespace with trim() before saving, so the code does the same.
  • Never display the password or write it to a log. Sanitizing functions (such as sanitize_text_field()) can change characters, so the value is used only for checking.
  • When you add an error in user_profile_update_errors, nothing is saved and the error appears on the edit screen. When you add an error in validate_password_reset, the error appears on the reset screen and the password is not changed.
  • Count characters with mb_strlen(). strlen() counts bytes, so passwords containing multibyte characters such as Japanese would be counted as longer than they are.

Test it

  1. Save it on a staging site, then on your own profile screen enter a short password (for example 10 characters), tick "Confirm use of weak password," save, and confirm that you get an error.
  2. Confirm that a password of 15 characters or more saves.
  3. Log out, open the reset screen from "Lost your password?", and run the same checks.

What it does not cover

  • WP-CLI's wp user update --user_pass= and wp user create do not go through these hooks, so they are not checked.
  • WooCommerce's "My account" screen and custom registration screens of membership plugins need their own checks through each plugin's hooks. Check each plugin's documentation for a hook that lets you validate passwords.
  • Weak passwords that are already set are not detected by this check (WordPress stores only password hashes, so their strength cannot be checked later). Replace them with the bulk reset below.

Bulk-reset passwords with WP-CLI

If you suspect unauthorized access or are introducing new requirements, you can regenerate passwords with wp user reset-password. The new passwords are random, and by default users receive a change notification email.

# Reset the passwords of all administrators (they receive a change notification email)
wp user reset-password $(wp user list --role=administrator --format=ids)

# Reset one user, send no email, and print only the new password
wp user reset-password editor01 --skip-email --porcelain

--show-password displays the new password on screen. --porcelain outputs only the password. If you hand the displayed password to the user, do not paste it directly into a chat or email body; have the user change it right after logging in.

Along with password requirements, also consider making two-factor authentication mandatory and limiting login attempts.

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲