Custom Reset Password Page WordPress

Custom Reset Password Page WordPress: Replace the Default WordPress Reset Screen (No Plugins)

Facebook
X
LinkedIn
WhatsApp
Email

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.

Picture of Ido Mosko

Ido Mosko

Ido is the content manager here at FixingWP. He is a WordPress enthusiast with extensive experience with plugin and theme customization, SEO, and marketing. My biggest hobbies are snowboarding, playing poker, and watching soccer.

Leave a Reply

Your email address will not be published. Required fields are marked *

Here are a few tips from us before you leave

Practical tips, examples, and best practices to keep your WordPress site safe.

Your website will thank us, you welcome :)