If your WordPress site uses a custom theme, the default “Lost your password?” page can feel out of place. This is the screen users reach from the login page and from the password reset email, and by default, it uses WordPress styling and layout.
With a small amount of PHP in your child theme, you can create a Custom reset password page that matches your branding, while still using WordPress core functions for the reset flow and security.
What You Are Going to Build
- A new page (example: “Reset Password”) that fits your design
- A redirect so requests to the default reset URL land on your page
- A reset form shortcode placed inside your page content
- No plugins, using native WordPress hooks
How the WordPress reset flow works (quick overview)
When a user requests a password reset, WordPress sends an email containing a reset link with a reset key. When that key is used, WordPress shows the reset form and messages (success, error, invalid key, and so on).
To keep everything working correctly, the goal is not to re-invent the reset logic. Instead, you redirect the user to your branded page and let WordPress render the reset form.
Step 1: Create a custom reset page
Create a new page in the WordPress admin and name it something sensible, such as:
- Reset Password
- Forgot Password
- Set a New Password
Note the page URL slug. You will need it when redirecting the default reset URL.
Step 2: Redirect the default reset URL to your custom page
Open your child theme and go to functions.php. Add the redirect code at the bottom (or in the appropriate section for your project).
/**
* 1) Change the default lost password URL everywhere in WordPress.
*/
add_filter('lostpassword_url', function ($lostpassword_url, $redirect) {
$url = home_url('/reset-password/');
if (!empty($redirect)) {
$url = add_query_arg('redirect_to', urlencode($redirect), $url);
}
return $url;
}, 10, 2);This redirect targets WordPress’ default “lost password” behavior and sends users to your custom page instead.
Important: Replace /reset-password/ with the actual slug of your custom page Why this works
The reset request link flows through WordPress’ login endpoints. By intercepting that login init process and redirecting, users immediately land on your branded page instead of the default WordPress screen.
Step 3: Add a shortcode that outputs the reset password form
Next, you need to display the actual reset form on your custom page. WordPress already provides the form output logic. The easiest approach is to create a shortcode that renders it.
Add the shortcode code to functions.php in your child theme.
/**
* 2) Shortcode: [custom_lost_password_form]
* Shows either:
* - request reset email form
* - set new password form (when key + login are present)
*/
add_shortcode('custom_lost_password_form', function () {
if (is_user_logged_in()) {
return '<p>You are already logged in.</p>';
}
$output = '';
// Handle "request reset email" form submit
if (
isset($_POST['custom_lost_pass_nonce']) &&
wp_verify_nonce($_POST['custom_lost_pass_nonce'], 'custom_lost_pass_action') &&
isset($_POST['user_login'])
) {
$result = retrieve_password(sanitize_text_field($_POST['user_login']));
if (is_wp_error($result)) {
foreach ($result->get_error_messages() as $message) {
$output .= '<p class="error">' . esc_html($message) . '</p>';
}
} else {
$output .= '<p class="success">Check your email for the password reset link.</p>';
}
}
// Handle "set new password" form submit
if (
isset($_POST['custom_reset_pass_nonce']) &&
wp_verify_nonce($_POST['custom_reset_pass_nonce'], 'custom_reset_pass_action') &&
!empty($_POST['rp_key']) &&
!empty($_POST['rp_login']) &&
!empty($_POST['pass1'])
) {
$user = check_password_reset_key(
sanitize_text_field($_POST['rp_key']),
sanitize_text_field($_POST['rp_login'])
);
if (is_wp_error($user)) {
$output .= '<p class="error">This reset link is invalid or has expired.</p>';
} else {
$pass1 = (string) $_POST['pass1'];
$pass2 = isset($_POST['pass2']) ? (string) $_POST['pass2'] : '';
if ($pass1 !== $pass2) {
$output .= '<p class="error">Passwords do not match.</p>';
} elseif (strlen($pass1) < 8) {
$output .= '<p class="error">Password must be at least 8 characters.</p>';
} else {
reset_password($user, $pass1);
$output .= '<p class="success">Your password has been reset. <a href="' . esc_url(wp_login_url()) . '">Log in</a></p>';
return $output;
}
}
}
// If user clicked email link, WordPress provides key + login in query args
$rp_key = isset($_GET['key']) ? sanitize_text_field($_GET['key']) : '';
$rp_login = isset($_GET['login']) ? sanitize_text_field($_GET['login']) : '';
// Show set new password form
if ($rp_key && $rp_login) {
$output .= '
<form method="post" class="custom-reset-password-form">
<p>
<label for="pass1">New password</label><br>
<input type="password" name="pass1" id="pass1" required>
</p>
<p>
<label for="pass2">Confirm new password</label><br>
<input type="password" name="pass2" id="pass2" required>
</p>
<input type="hidden" name="rp_key" value="' . esc_attr($rp_key) . '">
<input type="hidden" name="rp_login" value="' . esc_attr($rp_login) . '">
' . wp_nonce_field('custom_reset_pass_action', 'custom_reset_pass_nonce', true, false) . '
<p>
<button type="submit">Reset Password</button>
</p>
</form>';
return $output;
}
// Default lost password form
$output .= '
<form method="post" class="custom-lost-password-form">
<p>
<label for="user_login">Username or Email Address</label><br>
<input type="text" name="user_login" id="user_login" required>
</p>
' . wp_nonce_field('custom_lost_pass_action', 'custom_lost_pass_nonce', true, false) . '
<p>
<button type="submit">Get Reset Link</button>
</p>
</form>';
return $output;
});Because reset form output can vary depending on your setup and theme/plugins, adjust the renderer to ensure it outputs the actual “reset password” form (not the general login form). The key idea remains the same: use WordPress core output logic and place it into your page.
For most implementations, the shortcode name you choose is what matters. In the next step, you will paste it into the page content.
Step 4: Insert the shortcode into your custom reset page
Go back to your custom page, click Edit, and paste the shortcode into the page content:
[custom_lost_password_form]Then click Save or Update.
Security and reliability tips (don’t skip these)
When building a Custom reset password page in WordPress, the biggest risk is breaking reset key validation or making the reset flow inconsistent.
To stay safe:
- Use WordPress core functions and hooks where possible, instead of copying reset logic manually
- Preserve query parameters like the reset key
- Keep redirects narrow so you only redirect the “lostpassword” action
- Always test both the reset request step and the actual key-based reset step
Conclusion
With this setup, your password reset experience becomes consistent with the rest of your site, while still relying on WordPress core for the reset process. That is the real win of the custom reset password page, WordPress done the right way.