Customizing Wordpress Login URL

I would like change the default login URL for WordPress, i.e. www.website.com/wp-admin to something else that is custom and will be hard for visitors to try and hack their way in.

Topic wp-login-form Wordpress

Category Web


Theory

What you can do is, only allow access to your wp-login.php, when a special token is available and is validated to be true, otherwise you will die on the page with a status code of 403 or 404 (whatever you prefer).

To generate the token, you can set up another secret route on your site, generate the token here and redirect with that information to the wp-login.php.

Things you need to get this concept working are:

  1. Registering a custom route with add_rewrite_rule()
  2. Whitelisting the target query var of your route in the query_vars filter
  3. Set up a callback, which checks for the query var and eventually generates the token and redirects using wp_redirect()
  4. Hook into the wp-login.php to check if the token is there, it is valid, and it is, show the login page, otherwise die (Also, you have to make sure, the current page is the login page, since there is not a direct hook)

Tokens

Your token can either be fixed, or it could be dynamic and refreshes itself on every page view (more secure for sure).

To generate your tokens, you can use WordPress wp_create_nonce() and wp_verify_nonce() functions.

To pass the tokens from your route to wp-login.php page during the redirect, you could use one of these methods:

  • Attaching the token as GET parameter
  • Attaching the token as POST parameter
  • Attaching the token as HTTP header, for example Authorization
  • Attaching the token as HTTP cookie
  • Storing it as WordPress transient in the database
  • Storing inside a PHP session

Some listed options should be preferred above others, for example using PHP sessions inside WordPress is probably not the highest security standard: https://techgirlkb.guru/2017/08/wordpress-doesnt-use-php-sessions-neither/

Code Example

In this example, I have coded a combination of a token generation with WordPress nonces and a redirect with the attached token as GET parameter.

<?php

declare(strict_types=1);

use Devidw\WordPress\Helper\Helper;

class HardenLogin
{
    /**
     * Constructor.
     */
    public function __construct()
    {
        add_action(
            hook_name: 'init',
            callback: [$this, 'addCustomLoginPageUrl'],
        );

        add_filter(
            hook_name: 'query_vars',
            callback: [$this, 'addCustomLoginPageUrlQueryVar'],
        );

        add_action(
            hook_name: 'template_redirect',
            callback: [$this, 'addCustomLoginPageRoute'],
        );

        add_action(
            hook_name: 'init',
            callback: [$this, 'replaceDefaultLoginPage'],
            priority: PHP_INT_MAX,
        );
    }

    /**
     * Get the custom login page slug.
     */
    public function getCustomLoginPageSlug(): string
    {
        return 'my-super-secret-login-page';
    }

    /**
     * Add custom login page route.
     */
    public function addCustomLoginPageUrl(): void
    {
        add_rewrite_rule(
            regex: '^' . $this->getCustomLoginPageSlug() . '/?$',
            query: 'index.php?my_hard_login=1',
            after: 'top',
        );
    }

    /**
     * Add custom login page query var.
     */
    function addCustomLoginPageUrlQueryVar(array $vars): array
    {
        $vars[] = 'my_hard_login';
        return $vars;
    }

    /**
     * Add a custom login page.
     */
    public function addCustomLoginPageRoute(): void
    {
        if (get_query_var('my_hard_login', false) === false) {
            return;
        }

        wp_redirect(
            location: add_query_arg(
                'my_hard_login_nonce',
                wp_create_nonce('my_hard_login_nonce'),
                wp_login_url(),
            ),
        );

        die;
    }

    /**
     * Replace the default login page.
     */
    public function replaceDefaultLoginPage(): void
    {
        if (
            !Helper::isLoginPage() or
            !empty($_GET['action']) and $_GET['action'] === 'logout' and !empty($_GET['_wpnonce']) and wp_verify_nonce($_GET['_wpnonce'], 'log-out') === 1 or
            !empty($_GET['dw_hard_login_nonce']) and wp_verify_nonce($_GET['dw_hard_login_nonce'], 'dw_hard_login_nonce') === 1
        ) {
            return;
        }

        header('HTTP/1.0 404 Not Found');

        @include_once get_query_template('404');

        die;
    }
}

To determine, if we are on the login page, you can check out this answer: https://wordpress.stackexchange.com/a/237285/218274.

It basically is what Helper::isLoginPage() is doing: https://github.com/devidw/wp-helper/blob/ff2e15eb4514d6728993442ab22bba2f6659a2f5/src/Helper.php#L69-L81

Edit

To get things working, we actually have to extend the last method, by filtering the login URL, WordPress places in the login form, since our logic would prevent a login otherwise.

/**
 * When we are on the login page, we have to add the nonce, to the form[action*="wp-login.php"].
 */
if (Helper::isLoginPage()) {
    add_filter(
        hook_name: 'site_url',
        callback: [$this, 'filterLoginUrl'],
    );
}

And:

/**
 * Filter login URL.
 */
public function filterLoginUrl(string $url): string
{
    return add_query_arg(
        'my_hard_login_nonce',
        wp_create_nonce('my_hard_login_nonce'),
        $url,
    );
}

You can try this code, it works for me.

This code redirects to homepage whenever the default /wp-admin or /wp-login is accessed. You can set a passcode as the login URL and only allow wp-admin and wp-login access via this URL: https://www.yourdomain.com/?you-set-your-passcode

Add this code to the functions.php of your theme.

// define and set passcode that serves as login url.
define('PASSCODE','make-you-own-passcode');

function mask_login_url(){
// redirect to login page when passcode is verified
if( !is_user_logged_in() && parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY) == PASSCODE ){
    wp_safe_redirect( home_url('wp-login.php?'. PASSCODE .'&redirect=false') );
    exit();
}
// redirect to dashboard if user has already logged in
if( is_user_logged_in() && parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY) == PASSCODE ){
    wp_safe_redirect( home_url("wp-admin") );
    exit();
} } 
add_action( 'init', 'mask_login_url');

function mask_login_redirects(){
if( isset($_POST['passcode']) && $_POST['passcode'] == PASSCODE) return false;

// redirects to dashboard when /wp-admin is accessed and user is logged in
if ( (is_user_logged_in()) && (strpos($_SERVER['REQUEST_URI'], 'wp-admin') !== false)) {
    wp_safe_redirect( home_url("wp-admin"), 302 );
    exit();
}
// redirects to homepage when /wp-admin or /wp-login is accessed and user is not logged in
if ( (!is_user_logged_in()) && ((strpos($_SERVER['REQUEST_URI'], 'wp-admin') !== false) || (strpos($_SERVER['REQUEST_URI'], 'wp-login') !== false)) && ( strpos($_SERVER['REQUEST_URI'], PASSCODE) === false ) ) {
    wp_safe_redirect( home_url(), 302 );
    exit();
}
// redirect to homepage after logout
if( strpos($_SERVER['REQUEST_URI'], 'action=logout') !== false ){
    check_admin_referer( 'log-out' );
    wp_logout();
    wp_safe_redirect( home_url('?logged-out'), 302 );
    exit();
} }
add_action( 'login_init', 'mask_login_redirects', 1); 

// Add a passcode hidden field to login form
function custom_login_hidden_field(){
echo '<input type="hidden" name="passcode" value="'. PASSCODE .'" />';
}
add_action('login_form', 'custom_login_hidden_field');

Hello use following code without plugin:

Add Below code in wp-config.php

define('WP_ADMIN_DIR', 'private-area');  
define( 'ADMIN_COOKIE_PATH', SITECOOKIEPATH . WP_ADMIN_DIR);  

Add Below Code in theme functions.php

add_filter('site_url',  'change_wpadmin_url', 10, 3);  
function change_wpadmin_url( $url, $path, $orig_scheme ) {  
    $old  = array( "/(wp-admin)/");  
    $admin_dir = WP_ADMIN_DIR;  
    $new  = array($admin_dir);  
    return preg_replace( $old, $new, $url, 1);  
}

Add below line to .htaccess file

RewriteRule ^private-area/(.*) wp-admin/$1?%{QUERY_STRING} [L]

You can change the backend URL to custom with your own needs. You can acheive this using the plugin iThemes Security.
iThemes Security (formerly Better WP Security) gives you over 30+ ways to secure and protect your WordPress site.

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.