Blocking access to your WordPress websites /wp-login.php
page is a good way to add a layer of protection to your WordPress website. It’s not a foolproof approach, but it’s commonly used in cloud hosting platforms like AWS, Google Cloud and Azure as a secondary line of defense. The same approach can be used on your WordPress website with the following PHP class I’ve written.
The following class can be used in a custom theme or plugin, all you need to do is adjust the IP addresses in the $allowed
array so that it includes a whitelist of your own IP’s.
To get this working you’ll need to get this PHP class loading in your custom WordPress theme or plugin.
<?php
/**
* Limit Login Page Access by IP Address
*/
class Kevinleary_Login_IP_Restrict
{
/**
* Constructor
*/
public function __construct()
{
add_action( 'login_init', [$this, 'login_ip_restrict'] );
}
/**
* Authenticate
*
* Limit WordPress logins to specific IP addresses
* before the user has been authenticated with WordPress.
* People can spoof a connecting IP address, so this isn't
* foolproof, but it does provide an added layer of difficult
* for any unauthorized login attempts.
*/
public function login_ip_restrict()
{
// CloudFlare provided IP of user (WPEngine & Kinsta)
$ip_address = isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ? esc_attr( $_SERVER['HTTP_CF_CONNECTING_IP'] ) : null;
// Extract first IP when CloudFlare returns multiple
if ( $ip_address && strstr( $ip_address, ',' ) ) {
preg_match( '/([0-9.]+)/m', $ip_address, $matches, PREG_OFFSET_CAPTURE );
$ip_address = isset( $matches[0][0] ) ? $matches[0][0] : $ip_address;
}
// Fallbacks when HTTP_CF_CONNECTING_IP isn't available
if ( ! $ip_address ) {
$ip_address = ( ! isset( $_SERVER['REMOTE_ADDR'] ) || !$this->is_localhost( $_SERVER['REMOTE_ADDR'] ) ) ? esc_attr( $_SERVER['REMOTE_ADDR'] ) : null;
}
if ( ! $ip_address ) {
$ip_address = ( ! isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) || !$this->is_localhost( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ? esc_attr( $_SERVER['HTTP_X_FORWARDED_FOR'] ) : null;
}
// No IP address value available
if ( ! $ip_address ) {
$this->unauthorized();
}
// Validate IP address
if ( ! filter_var( $ip_address, FILTER_VALIDATE_IP ) ) {
$this->unauthorized();
}
// Verify connecting IP address is in the whitelist
$allowed = ['00.000.000.00'];
if ( ! in_array( $ip_address, $allowed ) ) {
$this->unauthorized();
}
}
/**
* Localhost IP Check
*/
public function is_localhost( $ip_address )
{
$localhosts = [
'127.0.0.1',
'::1',
];
return in_array( $ip_address, $localhosts );
}
/**
* Unauthorized Template
*/
public function unauthorized()
{
$site_name = get_bloginfo( 'site_name' );
ob_start();
?>
<style>
html {
background: #cf494442 !important;
}
#error-page {
padding: 4.5em 2em;
background: transparent;
border: none;
color: #cf4944;
box-shadow: none;
animation: fadeInAnimation ease 3s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
opacity: 0;
}
#error-page .wp-die-message {
margin: 0;
text-align: center;
}
#error-page h1 {
font-size: 48px;
font-weight: 800;
margin: 0;
padding: 0;
line-height: 1;
border: none;
color: #cf4944;
text-transform: uppercase;
}
#error-page p {
margin: 18px 0 0;
font-size: 31px;
font-weight: 300;
letter-spacing: 0.5px;
color: #cf4944;
}
@keyframes fadeInAnimation {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
<h1>Unauthorized</h1>
<p>You are not authorized to access this page.</p>
<?php
$error = ob_get_clean();
wp_die( $error, "$site_name: Unauthorized" );
}
}
new Kevinleary_Login_IP_Restrict();
This will check the connecting IP address of every visitor to the /wp-login.php
page before any HTML is output. This allows us to hijack the login, and replace it with an UNAUTHORIZED message/dialog to users that aren’t connecting from a valid IP we’ve whitelisted.
Using the same approach, we can also limit access to the WordPress admin. There are two methods I typically use for this, which one depends on the circumstance and requirements
You can test this locally before deploying it live by adjusting your nginx
configuration to set your localhost server’s connecting IP to an IP address that you want to whitelist.
Find the nginx .conf
file that contains a server
block for your website. It will usually look similar to this:
server {
server_name kevinleary.net;
listen 127.0.0.1:443 ssl;
...
}
Find the location
rule used for .php
files, which usually looks like this:
# PHP to FastCGI
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php;
include inc/fastcgi_params;
}
To spoof the IP of your localhost, you’ll need to:
$allowed
array that you want to testfastcgi_param
rule to set the HTTP_X_FORWARDED_FOR
header to be this IP addressThe adjusted configuration will look like this.
# PHP to FastCGI
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php;
fastcgi_param HTTP_X_FORWARDED_FOR 00.000.000.00;
include inc/fastcgi_params;
}
00.000.000.00
should be replaced with the IP address you’re testing.