Posted on

Hide Your wp-login Page from Bots

Hide your WordPress site’s wp-login.php page from bots. Either redirect to your site’s 404 page, or redirect to an alternative frontend login form. I prefer the 404 redirect myself, as it’s more confusing for the Bots 🙂

You’ll need to be using a custom child theme so you can make changes to your functions.php file.

Why Should We Hide the wp-login.php Page?

The Internet is full of Bots. Programs that constantly scan web sites looking for known vulnerabilities so they can hack their way in. Because WordPress is a ridiculously popular CMS, loads of Botnets target WordPress websites. If they find one that’s got a known dodgy plugin installed, or maybe an old version of WordPress, they’ll exploit that chink in the armour.

Another thing Botnets look for is weak password security. If a Bot can can get a list of a website’s users (enumerate the WordPress users), then it can loop through the usernames and try some well-known passwords, like “password1234”. If it takes 1 second to try a username/password combination then a Bot can make 3600 guesses every hour – that’s 79,800 guesses every day. The odds are that at least guess out of 79.800 will work with “password1234”.

BUT… If we can rig our website so that www.example.org/wp-login.php returns a “404 Not Found” page, the Bots will give up and start scanning the next website on its radar.

warningRedirecting your wp-login.php file to a “404 Not Found” page is not a magic way of making your website more secure. It’s a just a neat way of trying to reduce the number of Bot login attempts.

Let’s Write some Code

dangerMake sure you can login to your website without using the standard wp-login.php page! You can do this if you’re using WooCommerce, Ultimate Member, or any other plugin that provides its own way of logging-in.

In your custom child theme’s folder, make a new file called functions-redirect-login.php and paste the following into it.

<?php

/**
 * Redirect wp-login.php to either a 404 page or an alternative login URL from
 * something like WooCommerce.
 *
 * To enable the login redirect, put the following in your functions.php file.
 *
 * define('HW_IS_LOGIN_REDIRECT_ENABLED', true);
 *
 * If you want to redirect to an alternative URL instead of ot a 404, enable
 * the option like this:
 *
 * define('HW_IS_LOGIN_REDIRECT_TO_URL_ENABLED', true);
 *
 */

// Block direct access.
if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

function custom_get_is_showing_wp_login() {
	return (strpos($_SERVER["SCRIPT_NAME"], "/wp-login.php") === 0);
}

function custom_get_is_login_redirect_enabled() {
	return defined('HW_IS_LOGIN_REDIRECT_ENABLED') &&
		(HW_IS_LOGIN_REDIRECT_ENABLED === true);
}

function custom_get_login_redirect_to_url_enabled() {
	return defined('HW_IS_LOGIN_REDIRECT_TO_URL_ENABLED') &&
		(HW_IS_LOGIN_REDIRECT_TO_URL_ENABLED === true);
}

function custom_get_is_wp_login_being_attempted() {
	$request_uri = $_SERVER['REQUEST_URI'];
	return custom_get_is_showing_wp_login() &&
		(strpos($request_uri, 'action=') === false) &&
		(strpos($request_uri, 'checkemail=confirm') === false);
}

function custom_get_is_wp_lostpassword_being_attempted() {
	$request_uri = $_SERVER['REQUEST_URI'];
	return custom_get_is_showing_wp_login() && (
		(strpos($request_uri, 'action=lostpassword') !== false)
		||
		(strpos($request_uri, 'action=resetpass') !== false)
		||
		(strpos($request_uri, 'action=rp') !== false)
		||
		(strpos($request_uri, 'checkemail=confirm') !== false)
	);
}

function custom_get_login_redirect_url() {
	$login_redirect_url = null;

	// if ($is_login_redirect_to_url_enabled) {
	if (function_exists('wc_get_page_permalink')) {
		// WooCommerce login URL
		$login_redirect_url = wc_get_page_permalink('myaccount');
	} elseif (function_exists('UM') && !empty($login_page_id = UM()->options()->get('core_login'))) {
		// UltimateMember login URL
		$login_redirect_url = get_permalink($login_page_id);
	} else {
		// TODO. Check for BuddyPress login URL?
		// else...
		// TODO. Check for alternative login URL?
		// etc...
	}
	// }

	return $login_redirect_url;
}

function custom_redirect_login() {
	$is_login_redirect_enabled = custom_get_is_login_redirect_enabled();
	$is_login_redirect_to_url_enabled = custom_get_login_redirect_to_url_enabled();
	$is_login_being_attempted = custom_get_is_wp_login_being_attempted();

	if ($is_login_redirect_enabled && $is_login_being_attempted) {
		$login_redirect_url = null;
		if ($is_login_redirect_to_url_enabled) {
			$login_redirect_url = custom_get_login_redirect_url();
		}

		if (!empty($login_redirect_url)) {
			// Redirect to a an alternative URL.
			wp_redirect($login_redirect_url);
			exit;
		} else {
			// Redirect to a 404.
			$GLOBALS['wp_query']->set_404();
			status_header(404);
			nocache_headers();
			include get_query_template('404');
			exit;
		}
	}
}
add_action('init', 'custom_redirect_login');

function custom_login_url($login_url, $redirect, $force_reauth) {
	$is_login_redirect_enabled = custom_get_is_login_redirect_enabled();
	$is_lost_password_being_attempted = custom_get_is_wp_lostpassword_being_attempted();

	if ($is_login_redirect_enabled &&
		$is_lost_password_being_attempted &&
		!empty($login_redirect_url = custom_get_login_redirect_url())
	) {
		$login_url = $login_redirect_url;
	}

	return $login_url;
}
add_filter('login_url', 'custom_login_url', 10, 3);

Then you just include this file from your custom child theme’s functions.php file, like this:

// Redirect wp-login.php to our standard 404 page.
define('HW_IS_LOGIN_REDIRECT_ENABLED', true);
require_once 'functions-redirect-login.php';

The logic is quite straightforward and runs like this:

  • Is someone trying to load wp-login.php right now?
  • If HW_IS_LOGIN_REDIRECT_ENABLED is enabled, then figure out if we’re redirecting to an alternative login URL, or to the sites’ 404 page.
  • If someone is trying to reset their password then DON’T do a redirect – just let WordPress work normally. There’s probably a cleaner way to do this check. Feel free to comment below!
  • If we’re redirecting to a URL then try to find a URL by checking for WooCommerce, UltimateMember, etc.
  • Then we just do the actual redirect.

You’ll see near the top where we’re setting the value for is_login_attempted, we check to see if “action=” is specified in the URL. If “action=” is somewhere in the query string then we don’t do any of the redirect stuff. This means that you can still do things like this without redirecting…

/wp-login.php?action=lostpassword

…or even this…

/wp-login.php?action=blahblahblah

…will let you get back to the standard login page if you need to.

importantDo some testing. I’ve not found any problems with this mod, but you should make sure that the Reset Password link still works for your members.

That’s it for this little WordPress snippet. Have fun fooling those Bots! 😎

Leave a Reply

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