Type something to search...
How to protect website login pages from bots?

How to protect website login pages from bots?

You protect website login pages from bots by making automated attempts slow, expensive, and pointless: rate limit login requests per IP and per account, add a privacy-friendly CAPTCHA like Cloudflare Turnstile or hCaptcha, filter known bad traffic with a web application firewall, ban repeat offenders with a tool like Fail2Ban, and require two-factor authentication or passkeys so a guessed password alone is useless. Layering these defences stops the vast majority of login bots without annoying real users.

If you've ever looked at your server logs or a security plugin's dashboard, you've probably seen thousands of failed logins from IP addresses all over the world. That's normal on the modern web, but it isn't harmless. This article explains what login bots are trying to do, then walks you through each layer of defence, from quick plugin settings to server-level rules, with working examples for WordPress and custom applications.

What Are Login Bots Doing?

Login bots are automated scripts that repeatedly submit username and password combinations to your login form. They typically fall into a few categories:

  • Brute force attacks: Guessing common passwords like password123 against common usernames like admin.
  • Credential stuffing: Using real username and password pairs leaked in other breaches, betting that people reuse passwords.
  • Password spraying: Trying one or two common passwords across many accounts, staying under per-account lockout limits.
  • Account enumeration: Probing to find which usernames or emails exist, often through different error messages.

Modern bots are distributed across large numbers of IP addresses, including residential proxies, and some run in real headless browsers that execute JavaScript. That's why a single defence, like blocking one IP, rarely works on its own.

Why It Matters Even If Your Passwords Are Strong

Even when bots never succeed, they still cost you:

  • Server load: Each login attempt runs PHP and database queries. Thousands of them can slow or crash a small site.
  • Account lockouts: Aggressive lockouts can lock out legitimate users whose usernames are targeted.
  • Noise: A flood of failed logins hides the one attempt that actually matters.
  • Risk for weaker accounts: Your admin might have a strong password, but an old editor account might not.

Layer 1: Rate Limiting

Rate limiting restricts how many login attempts can be made in a given time. It's the foundation of bot protection because it makes guessing extremely slow.

Limit Attempts in WordPress

The easiest option is a plugin. Well-known choices include:

  • Limit Login Attempts Reloaded: Lightweight and focused on lockouts.
  • Wordfence: Includes brute force protection, lockouts, and blocking of commonly leaked passwords.
  • Solid Security: Offers lockouts and network-wide brute force protection.

Typical sensible settings are four to five failed attempts before a lockout of 20 to 60 minutes, with longer lockouts for repeated offenders.

Limit Attempts in Your Own Code

If you're building a custom login, you can implement a basic per-IP and per-username limit. Here's a WordPress example using transients, suitable for a custom plugin:

<?php
add_filter( 'authenticate', 'sajjad_check_login_rate_limit', 30, 3 );
function sajjad_check_login_rate_limit( $user, $username, $password ) {
    if ( empty( $username ) ) {
        return $user;
    }

    $ip_key   = 'sajjad_login_ip_' . md5( sajjad_client_ip() );
    $user_key = 'sajjad_login_user_' . md5( strtolower( $username ) );

    if ( (int) get_transient( $ip_key ) >= 10 || (int) get_transient( $user_key ) >= 5 ) {
        return new WP_Error(
            'too_many_attempts',
            __( 'Too many login attempts. Please try again later.', 'sajjad' )
        );
    }

    return $user;
}

add_action( 'wp_login_failed', 'sajjad_record_failed_login' );
function sajjad_record_failed_login( $username ) {
    $ip_key   = 'sajjad_login_ip_' . md5( sajjad_client_ip() );
    $user_key = 'sajjad_login_user_' . md5( strtolower( $username ) );

    set_transient( $ip_key, (int) get_transient( $ip_key ) + 1, 15 * MINUTE_IN_SECONDS );
    set_transient( $user_key, (int) get_transient( $user_key ) + 1, 15 * MINUTE_IN_SECONDS );
}

function sajjad_client_ip() {
    // Only trust REMOTE_ADDR unless your server is configured to restore the real IP behind a proxy.
    return isset( $_SERVER['REMOTE_ADDR'] )
        ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) )
        : '0.0.0.0';
}

Limiting per username as well as per IP helps against distributed attacks that rotate IP addresses. Be aware that per-username limits can let an attacker deliberately lock out a real user, so keep the lockout short. Established plugins handle these edge cases more thoroughly, so for most sites a plugin is the better choice.

Rate Limit at the Web Server

Server-level limits stop requests before PHP even runs. In Nginx, define a zone in the http block:

limit_req_zone $binary_remote_addr zone=login:10m rate=6r/m;

Then apply it to your login endpoint inside the server block:

location = /wp-login.php {
    limit_req zone=login burst=4 nodelay;
    limit_req_status 429;

    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Adjust the PHP-FPM socket to your version, test with sudo nginx -t, and reload with sudo systemctl reload nginx.

Layer 2: CAPTCHA and Bot Challenges

A CAPTCHA forces the client to prove it's a human, or at least a real browser, before the login is processed. Modern options are much less annoying than the old distorted-text puzzles:

  • Cloudflare Turnstile: Free, privacy-friendly, and usually invisible to real users. It doesn't require your site to use Cloudflare.
  • hCaptcha: Privacy-focused with free and paid tiers.
  • Google reCAPTCHA v3 or v2: Widely supported, though it involves sending data to Google, which has privacy implications in some regions.

For WordPress, plugins like Simple Cloudflare Turnstile, hCaptcha for WordPress, and Advanced Google reCAPTCHA add these challenges to the login, registration, and password reset forms. Many security plugins also include CAPTCHA options.

A CAPTCHA works best alongside rate limiting. On its own, some bots use CAPTCHA-solving services, but combined with rate limits, the cost of each attempt goes up considerably.

Verifying Turnstile on the Server

If you add Turnstile to a custom login form, the widget adds a token to the form, and you must verify it on the server. In WordPress:

<?php
function sajjad_verify_turnstile( $token ) {
    $response = wp_remote_post(
        'https://challenges.cloudflare.com/turnstile/v0/siteverify',
        array(
            'timeout' => 10,
            'body'    => array(
                'secret'   => SAJJAD_TURNSTILE_SECRET,
                'response' => $token,
                'remoteip' => sajjad_client_ip(),
            ),
        )
    );

    if ( is_wp_error( $response ) ) {
        return false;
    }

    $body = json_decode( wp_remote_retrieve_body( $response ), true );
    return ! empty( $body['success'] );
}

Define SAJJAD_TURNSTILE_SECRET in wp-config.php rather than hard-coding it, and decide in advance whether to fail open or closed if Cloudflare's API is unreachable.

Layer 3: Web Application Firewall and Edge Protection

A web application firewall (WAF) filters traffic before it reaches your server. Cloud-based options such as Cloudflare, Sucuri, and similar services can:

  • Block traffic from known malicious IPs and botnets.
  • Challenge suspicious clients with a browser check.
  • Rate limit login POST requests at the edge.
  • Apply rules by country, ASN, or user agent.

For example, in Cloudflare you can create a rate limiting rule that matches POST requests to /wp-login.php and blocks IPs that exceed a small threshold per minute. You can also add a custom rule that shows a Managed Challenge on the login page to everyone except your office IP.

Application-level firewalls, such as the one in Wordfence, run inside WordPress and can also block malicious login attempts, though they still use server resources for each request.

Layer 4: Ban Repeat Offenders With Fail2Ban

If you manage your own server, Fail2Ban watches log files and temporarily blocks IP addresses that show malicious behaviour. For WordPress, one approach is to log failed logins to the system log with a small plugin, then have Fail2Ban watch for those messages.

A minimal plugin that logs failures:

<?php
/**
 * Plugin Name: Sajjad Login Failure Logger
 */
add_action( 'wp_login_failed', 'sajjad_log_failed_login_to_syslog' );
function sajjad_log_failed_login_to_syslog( $username ) {
    $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown';
    openlog( 'wordpress', LOG_NDELAY | LOG_PID, LOG_AUTH );
    syslog( LOG_NOTICE, "Authentication failure for WordPress login from {$ip}" );
    closelog();
}

A Fail2Ban filter at /etc/fail2ban/filter.d/wordpress-login.conf:

[Definition]
failregex = ^.*wordpress(?:\[\d+\])?: Authentication failure for WordPress login from <HOST>$
ignoreregex =

And a jail in /etc/fail2ban/jail.d/wordpress.local:

[wordpress-login]
enabled  = true
filter   = wordpress-login
logpath  = /var/log/auth.log
maxretry = 5
findtime = 10m
bantime  = 1h
port     = http,https

On RHEL-based systems, the auth log is usually /var/log/secure, and on some modern systems you may need backend = systemd instead of a logpath. Test your filter with sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/wordpress-login.conf before enabling it, and add your own IP to ignoreip so you can't ban yourself. If your site sits behind Cloudflare or another proxy, make sure real visitor IPs are restored first, or Fail2Ban will ban the proxy.

The WP fail2ban plugin offers a ready-made version of this setup with more options.

Layer 5: Make Stolen Passwords Useless

Rate limiting and CAPTCHAs slow bots down, but the strongest protection is making a correct password insufficient on its own.

  • Two-factor authentication: Even if a bot guesses or stuffs the right password, it can't complete the login without the second factor.
  • Passkeys: These replace passwords with cryptographic keys, so there's nothing to guess or stuff.
  • Block breached passwords: Wordfence and some other tools can prevent users from choosing passwords that appear in known breach lists.

Layer 6: Reduce What Bots Can Learn and Reach

A few smaller measures reduce the attack surface for login bots:

  1. Use generic error messages: Say "Incorrect username or password" rather than revealing whether the username exists. WordPress reveals this by default, and you can change it with the snippet below.
  2. Protect other login paths: Bots also target xmlrpc.php, which allows many password guesses in a single request through system.multicall. Disable it if you don't use it, or block it at the WAF.
  3. Change the default login URL: A plugin like WPS Hide Login moves the login page, which dramatically reduces bot traffic, though it shouldn't be your only defence.
  4. Protect registration and password reset forms: Bots abuse these too, so apply the same CAPTCHA and rate limits.

To replace WordPress's detailed login errors with a generic message, add this to a custom plugin or your child theme's functions.php:

<?php
add_filter( 'login_errors', 'sajjad_generic_login_error' );
function sajjad_generic_login_error() {
    return __( 'Incorrect username or password.', 'sajjad' );
}

Monitoring and Tuning

After setting up your defences, keep an eye on how they perform:

  • Check your security plugin's lockout log for patterns, such as the same usernames being targeted.
  • Watch for false positives, like staff being locked out from the office network.
  • Review WAF events to see how much traffic is being blocked before it reaches you.
  • Alert on successful logins from new locations, which may indicate a compromised account.

FAQ: Protecting Login Pages From Bots

Install a reputable security plugin that limits login attempts, such as Limit Login Attempts Reloaded or Wordfence, and add a CAPTCHA like Cloudflare Turnstile. Then enable two-factor authentication for all administrators.

Not on its own. Some bots use CAPTCHA-solving services. Combine CAPTCHA with rate limiting, a WAF, and two-factor authentication for strong protection.

Occasionally, if the threshold is too low or many people share one IP address. Use reasonable limits, short lockouts, and an allowlist for trusted IPs such as your office.

It greatly reduces automated attempts against the default URL, but determined bots can still find the new one. Treat it as noise reduction, not a primary defence.

If you don't use the WordPress mobile app, Jetpack, or remote publishing tools that depend on it, blocking or disabling XML-RPC removes a common brute force target.

Credential stuffing is when bots try username and password pairs leaked from other websites, hoping people reused them. Unique passwords and two-factor authentication are the best defence.


Conclusion

Login bots are a constant background presence on the internet, but they don't have to be a threat. The winning approach is layered: rate limiting to slow attackers down, CAPTCHAs and WAF rules to filter automated traffic, Fail2Ban or edge blocking to remove repeat offenders, and two-factor authentication or passkeys so that even a correct password isn't enough.

Start with a security plugin and a CAPTCHA, then add server or edge-level rate limiting as your setup allows. Keep an eye on your logs, tune thresholds to avoid locking out real users, and make sure every privileged account uses a second factor. With those layers in place, bots can knock on your door as much as they like without getting in.

Tags :
Share :

Related Posts

What are the best WordPress security plugins?

What are the best WordPress security plugins?

The best WordPress security plugins for most sites are Wordfence, Sucuri Security, Solid Security, MalCare, All-In-One Security (AIOS), Patchstack, a

Dive Deeper
What are the most common website security threats?

What are the most common website security threats?

The most common website security threats are vulnerable or outdated software, weak and stolen passwords, malware infections, injection attacks like S

Dive Deeper
How does GDPR affect website security?

How does GDPR affect website security?

GDPR affects website security by turning it from a good habit into a legal obligation. If your website collects personal data from people in the EU (

Dive Deeper