Restrict WordPress Login by IP  Address

Block access to the wp-login page based on the connecting user's IP address

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.

Restrict Login by IP Address

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.

PHP Class

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();

How It Works

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.

Restrict Access to the WordPress Admin by IP Address

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

Testing Locally

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.

1. Open Your Nginx Server Config

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;
  ...
}

2. Adjust PHP Location Block

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;
}

Add a FastCGI Param

To spoof the IP of your localhost, you’ll need to:

  1. Copy the IP address from your $allowed array that you want to test
  2. Add a fastcgi_param rule to set the HTTP_X_FORWARDED_FOR header to be this IP address

The 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.

Related Articles

Meet the Author

Kevin Leary, WordPress Consultant

I'm a custom WordPress web developer and analytics consultant in Boston, MA with 16 years of experience building websites and applications. View a portfolio of my work or request an estimate for your next project.