How to block WordPress login access by Country

Stopping 90% of brute force attacks with a simple geo-fence logic.

Most wordpress site owners are used to seeing their logs flooded with thousands of failed login attempts from bots located all over the world. While standard security plugins do their job, they often feel like overkill or add unnecessary weight to your site.

I decided to take a different approach, why let a bot even try a password if it’s coming from a country where i don’t have any users or editors? the idea is to intercept the request at the front door. Instead of relying on a heavy plugin, we’re going to use a simple php function and the ip-api.com service to verify the visitor’s location. If the ip doesn’t match our allowed list, the login page simply won’t load for them. It’s a lightweight, surgical way to cut out the noise and keep your admin dashboard invisible to the wrong people.

The Code

Add the following snippet to your theme’s functions.php file or a custom functionality plugin:

/**
 * Block WordPress Login Access by Country (No Plugin)
 * Focus: Security & Performance
 */
function custom_block_login_by_country() {
    // 1. Only execute logic on the login page to save server resources
    global $pagenow;
    if ($pagenow !== 'wp-login.php') {
        return;
    }

    // 2. Define your ALLOWED country codes (ISO 3166-1 alpha-2)
    // List: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
    $allowed_countries = ['RO', 'US', 'GB'];

    // 3. Identify the real User IP (Handles Cloudflare and Proxies)
    $user_ip = $_SERVER['REMOTE_ADDR'];
    if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $user_ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
    } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $user_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
    }

    // 4. Query the Geo-IP API
    // We use ip-api.com (Free tier allows 45 requests/minute)
    $api_url = "http://ip-api.com/json/{$user_ip}?fields=status,countryCode";
    $response = wp_remote_get($api_url, ['timeout' => 5]);

    // Safety fallback: If API is down, allow access to prevent lockouts
    if (is_wp_error($response)) {
        return;
    }

    $data = json_decode(wp_remote_retrieve_body($response), true);

    // 5. Compare Country Code and Restrict Access
    if ($data && $data['status'] === 'success') {
        if (!in_array($data['countryCode'], $allowed_countries)) {
            // Send a 403 Forbidden header and a message
            wp_die(
                "Access Denied: Your location (" . esc_html($data['countryCode']) . ") is not authorized to access this page.",
                "Forbidden",
                ["response" => 403]
            );
        }
    }
}
// Hooking into 'init' ensures the check happens before the page is rendered
add_action('init', 'custom_block_login_by_country');

Depending on your WordPress version and theme, the file editor can be found in one of two places:

  • Option A (Classic Themes): Go to Appearance > Theme File Editor.
  • Option B (Block Themes / Newer WP): Go to Tools > Theme File Editor.

Why This Approach?

  • Zero Bloat: You don’t need a heavy security plugin running 24/7.
  • Performance: The code only runs on wp-login.php, so it doesn’t slow down the rest of your site.
  • Cloudflare Ready: It correctly identifies the visitor’s IP even if you use a CDN.

Important Notes

  • Exclude from Cache: Ensure your caching plugin (WP Rocket, LiteSpeed, etc.) is configured to never cache wp-login.php. Most plugins do this by default, but double-checking is key.
  • The 45 req/min Limit: The free version of ip-api.com is perfect for small to medium sites. If your login page is under a massive bot attack, the API might throttle requests. Our code includes a “fail-safe” that allows access if the API is overwhelmed.
  • Don’t Lock Yourself Out: If you travel to a country not on your $allowed_countries list, you will be blocked! Always keep a VPN handy or have FTP access to disable the function if needed.

⚠️ Disclaimer

Read this before proceeding:

  • Use at your own risk: This tutorial is provided for educational purposes only. Modifying your site’s code can cause errors if not done correctly. I am not responsible for any site crashes or lockouts resulting from this guide.
  • Backup First: Always create a full backup of your functions.php file or your entire website before adding custom code.
  • Don’t Lock Yourself Out: Ensure your own country is included in the $allowed_countries array. If you make a mistake, you will need to access your server via FTP or File Manager to manually remove the code.
  • Third-Party API: This method relies on an external service (ip-api.com). If their service is down or reaches its rate limit, the script is designed to fail-safe (allow access), but external dependencies always carry a small risk.

Comments

Leave a Reply

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