Opens in a new tab
  1. Home
  2. Guides
  3. Access
Access guide

How to limit login attempts in WordPress

Learn how to limit WordPress login attempts using native authentication hooks, temporary counters and lockouts, understand IP-based and account-based rate limiting, handle proxies safely and combine login limits with 2FA, monitoring and account security.

  • Updated September 22, 2026
  • 25 min read
  • WordPress guide

Limiting login attempts in WordPress helps reduce automated password guessing by controlling how many failed authentication attempts a source or account can make within a defined period.

WordPress validates usernames and passwords, but a standard installation does not provide one universal built-in configuration for:

maximum failed attempts
+
observation window
+
temporary lockout
+
progressive lockout

That means a public login endpoint can continue receiving repeated authentication attempts unless another layer applies rate limiting.

A login-attempt limiter can operate at several levels:

  • CDN or WAF;
  • web server;
  • reverse proxy;
  • hosting security layer;
  • WordPress application layer;
  • identity provider.

The best implementation depends on the traffic volume, hosting architecture and authentication paths that need protection.

This guide explains how to limit WordPress login attempts safely, how failed-attempt counters and lockouts work, how to build a simple WordPress-level limiter, why IP-only controls have limitations, how progressive lockouts differ from fixed ones and how rate limiting should work together with monitoring, two-factor authentication and account security.

Why limit WordPress login attempts?

A normal login form needs to accept failed passwords because legitimate users make mistakes.

The problem appears when failures can continue at machine speed:

attempt 1
attempt 2
attempt 3
attempt 4
attempt 5
attempt 6
...
attempt 5000

Automated systems can use repeated requests for:

  • password guessing;
  • credential stuffing;
  • testing leaked passwords;
  • trying common administrative usernames;
  • testing predictable account identifiers;
  • generating unnecessary server load.

The official WordPress documentation on brute-force attacks recommends rate limiting at the edge, web-server or application layer as one of the defenses against repeated login attempts.

For a deeper explanation of the attack itself, see WordPress Brute-Force Attacks, Explained.

What does “limit login attempts” actually mean?

A useful rate-limiting policy normally needs at least three values:

Threshold
→ how many failures are allowed

Observation window
→ over what period failures count

Lockout duration
→ how long further attempts are blocked

For example:

Maximum failures:
5

Observation window:
15 minutes

Lockout:
10 minutes

The resulting behavior becomes:

failure 1
failure 2
failure 3
failure 4
failure 5
↓
temporary lockout
↓
10 minutes
↓
authentication allowed again

Rate limiting is not the same as permanent blocking

A temporary lockout and an IP blacklist solve different problems.

Rate limiting
→ temporary automated response
to repeated attempts

Blacklist
→ explicit access rule
for a selected source

For permanent and trusted IP rules, see WordPress IP Blacklist and Whitelist Guide.

Rate limiting is not login monitoring

Monitoring records activity.

Rate limiting changes what happens when activity exceeds a threshold.

Monitoring
→ what happened?

Rate limiting
→ should another attempt
be accepted right now?

The two systems become much more useful together.

See How to Monitor WordPress Login Attempts for the logging side.

Rate limiting is one security layer

Limiting attempts does not replace:

  • strong passwords;
  • two-factor authentication;
  • account auditing;
  • least privilege;
  • secure sessions;
  • software updates;
  • network security.

The broader model is covered in WordPress Login Security Layers, Explained.

Where should login rate limiting happen?

A WordPress request can travel through several systems:

Internet
↓
CDN / WAF
↓
reverse proxy
↓
web server
↓
PHP
↓
WordPress
↓
authentication

A request blocked at the first layer consumes fewer origin resources than one rejected after WordPress has completely loaded.

Edge or WAF rate limiting

For large-scale abusive traffic, limiting requests at a CDN or web application firewall is often preferable.

The official WordPress brute-force guidance recommends edge or web-server throttling where possible because abusive requests can be stopped before PHP and WordPress do substantial work.

This becomes particularly relevant when a site receives:

hundreds
or
thousands

of login requests in a short period.

WordPress-level rate limiting

Application-level limiting is still useful because it can understand WordPress authentication events.

A plugin can know:

  • when authentication failed;
  • which login identifier was attempted;
  • whether an IP is trusted;
  • whether an existing lockout is active;
  • which error should be displayed;
  • which events should be logged.

This is harder for a generic network firewall to understand without additional configuration.

Use both layers when appropriate

A high-traffic WordPress installation might use:

WAF
→ broad request-rate protection

WordPress limiter
→ authentication-aware policy

2FA
→ account protection

login monitoring
→ operational visibility

These controls reinforce each other rather than competing for ownership of the same problem.

How WordPress authentication exposes failed logins

WordPress fires:

wp_login_failed

after authentication fails.

The official wp_login_failed hook documentation describes the event and provides:

$username
$error

to callbacks.

The username value can also contain an email address because standard WordPress authentication accepts either identifier.

Use wp_login_failed to increment counters

Conceptually:

authentication fails
↓
wp_login_failed fires
↓
identify rate-limit bucket
↓
increase failed-attempt count
↓
threshold reached?
↓
create temporary lockout

This is a clean point for recording a failed authentication event because the result is already known.

Use authenticate to stop locked-out requests

WordPress exposes the:

authenticate

filter before the standard authentication result is finalized.

The official authenticate reference explains that a callback may return:

WP_User
WP_Error
or
null

A login limiter can therefore check whether a source is currently locked and return a WP_Error before normal credential validation continues.

The authenticate filter receives the password

This requires care.

The filter parameters include:

$username
$password

Your rate limiter should not need to inspect, store or log the submitted password.

Never write it to:

  • PHP logs;
  • WordPress options;
  • database tables;
  • debug output;
  • external analytics.

For the wider authentication lifecycle, see WordPress Login Hooks Explained.

A simple WordPress rate-limiting architecture

A basic application-level limiter can use:

wp_login_failed
→ increase counter

authenticate
→ check lockout

WordPress Transients API
→ temporary state

The Transients API is useful because failed-attempt counters and lockouts naturally expire.

What is a WordPress transient?

A transient is temporary WordPress data associated with an expiration time.

The main functions are:

set_transient()
get_transient()
delete_transient()

The official set_transient(), get_transient() and delete_transient() references document this API.

Transients are useful for temporary lockouts

A failed-attempt key might look conceptually like:

login_attempts_7d493a...

while a lockout might use:

login_lockout_7d493a...

The random-looking suffix can be derived from the source rather than storing the raw address inside the option name.

Create a stable key from the source IP

function project_login_key(
    $prefix,
    $ip
) {

    $hash = hash_hmac(
        'sha256',
        $ip,
        wp_salt( 'auth' )
    );

    return $prefix . '_' . $hash;
}

This produces a deterministic key while avoiding a raw IP address directly in the transient name.

Retrieve the remote IP conservatively

function project_login_remote_ip() {

    if (
        empty(
            $_SERVER['REMOTE_ADDR']
        )
    ) {
        return '';
    }

    $ip = wp_unslash(
        $_SERVER['REMOTE_ADDR']
    );

    if (
        false === filter_var(
            $ip,
            FILTER_VALIDATE_IP
        )
    ) {
        return '';
    }

    return $ip;
}

This example intentionally does not automatically trust forwarding headers.

Why not automatically use X-Forwarded-For?

Sites behind a CDN, reverse proxy or load balancer require infrastructure-aware client-IP handling.

A request header such as:

X-Forwarded-For

should not be trusted merely because it exists.

The surrounding proxy architecture must define which systems are trusted to create or overwrite that header.

Otherwise an attacker may be able to supply a misleading value.

The same issue matters for permanent IP controls in the WordPress IP Blacklist and Whitelist Guide.

Implement the failed-attempt counter

A simplified example can count failures per IP:

add_action(
    'wp_login_failed',
    'project_record_failed_login',
    10,
    2
);

function project_record_failed_login(
    $username,
    $error
) {

    $ip = project_login_remote_ip();

    if ( ! $ip ) {
        return;
    }

    $attempt_key = project_login_key(
        'project_login_attempts',
        $ip
    );

    $lockout_key = project_login_key(
        'project_login_lockout',
        $ip
    );

    $attempts = get_transient(
        $attempt_key
    );

    if ( false === $attempts ) {
        $attempts = 0;
    }

    $attempts++;

    $max_attempts = 5;

    if (
        $attempts >= $max_attempts
    ) {

        set_transient(
            $lockout_key,
            1,
            10 * MINUTE_IN_SECONDS
        );

        delete_transient(
            $attempt_key
        );

        return;
    }

    set_transient(
        $attempt_key,
        $attempts,
        15 * MINUTE_IN_SECONDS
    );
}

This example creates:

threshold
→ 5 failures

observation window
→ 15 minutes

lockout
→ 10 minutes

Check the lockout before normal authentication

add_filter(
    'authenticate',
    'project_check_login_lockout',
    5,
    3
);

function project_check_login_lockout(
    $user,
    $username,
    $password
) {

    $ip = project_login_remote_ip();

    if ( ! $ip ) {
        return $user;
    }

    $lockout_key = project_login_key(
        'project_login_lockout',
        $ip
    );

    if (
        false !== get_transient(
            $lockout_key
        )
    ) {
        return new WP_Error(
            'login_rate_limited',
            __(
                'Too many login attempts. Please try again later.',
                'project'
            )
        );
    }

    return $user;
}

The submitted password is received by the filter because that is how WordPress defines the hook, but the rate limiter never reads or stores it.

Use a generic lockout message

The message:

Too many login attempts.
Please try again later.

is preferable to revealing unnecessary account information such as:

The administrator account
has been locked for 10 minutes.

Authentication messages can create account-enumeration signals when they reveal whether a specific identity exists.

The OWASP Authentication Cheat Sheet recommends generic authentication responses where practical.

For WordPress-specific error behavior, see WordPress Login Error Messages Explained.

Do not copy the example blindly into production

The code above illustrates the architecture.

A production implementation still needs decisions about:

  • proxy handling;
  • whitelisted addresses;
  • distributed attempts;
  • logging;
  • Multisite;
  • custom authentication systems;
  • lockout recovery;
  • persistent object caches;
  • high-volume attacks.

Transients may use an external object cache

On installations using a persistent object cache, WordPress may store transient values outside the normal wp_options table.

Your implementation should use the Transients API rather than assuming where the data physically lives.

Expiration is a maximum lifetime, not a real-time scheduler

A transient expiration tells WordPress when the value should no longer be considered valid.

It is not a background timer that executes code at the exact second the lockout ends.

On the next relevant request, an expired transient is treated as unavailable.

Fixed lockouts

A fixed lockout always uses the same duration:

5 failures
↓
10-minute lockout

5 more failures later
↓
10-minute lockout

This is easy to explain and predictable for legitimate users.

Progressive lockouts

A progressive policy increases the duration after repeated lockout cycles.

For example:

first lockout
→ 5 minutes

second lockout
→ 10 minutes

third lockout
→ 20 minutes

fourth lockout
→ 40 minutes

This increases the cost of continued automated guessing without immediately creating a permanent block.

Exponential lockout

A simple model can be:

duration =
base duration × 2^round

For example, with a five-minute base:

round 0
→ 5 minutes

round 1
→ 10 minutes

round 2
→ 20 minutes

round 3
→ 40 minutes

Set a maximum duration so a configuration bug or very high round count does not create unreasonable lockouts.

OWASP recognizes progressive lockout strategies

The OWASP Authentication Cheat Sheet discusses exponential lockout as one possible approach to repeated authentication failures.

It also emphasizes the need to balance security with usability and denial-of-service risk.

Account-based vs IP-based rate limiting

This is one of the most important design choices.

An IP-based limiter groups attempts by network source:

203.0.113.20
→ 5 failures
→ IP locked

An account-based limiter groups them by identity:

user@example.com
→ 5 failures
→ account locked

Both have limitations.

Limitations of IP-based rate limiting

An attacker can distribute attempts across:

IP A
IP B
IP C
IP D
IP E

and avoid reaching the threshold on any individual source.

IP-only limiting is therefore weaker against distributed attacks.

Shared IP addresses complicate the problem

A single address can represent:

  • an office;
  • a school;
  • a household;
  • a VPN exit node;
  • a mobile carrier;
  • a shared corporate gateway.

One user’s repeated failures can therefore affect other people using the same public IP.

Limitations of account-based lockouts

If an attacker knows a valid username, they can deliberately send bad passwords until that account reaches its lockout threshold.

The result becomes:

attacker does not gain access
but
legitimate user is denied access

This is a denial-of-service risk.

OWASP recommends thinking beyond IP alone

The OWASP authentication guidance recommends associating failed-login counters with the account rather than relying only on source IP, particularly because attackers can rotate addresses.

At the same time, it explicitly warns that account lockout can itself be abused to deny access.

For many production systems, the answer is not choosing one signal and pretending the problem vanished.

Combine signals where the system warrants it

A more advanced limiter can consider:

source IP
+
account identifier
+
failure rate
+
time window
+
previous lockout history

For example:

per-IP threshold
→ limits one source

per-account threshold
→ detects distributed targeting

global threshold
→ detects system-wide spikes

Complexity should match the risk and traffic level of the site.

Do not store passwords while building richer signals

Additional rate-limiting dimensions do not require:

  • password values;
  • authentication cookies;
  • 2FA codes;
  • password-reset tokens.

Keep credential material out of the limiter entirely.

Identifier privacy matters too

If you create per-account counters for attempted usernames or email addresses, consider whether those identifiers need to be stored directly.

A keyed hash can provide repeatable correlation:

identifier
↓
HMAC
↓
stable counter key

without putting the raw email address directly into a transient name.

Rate limiting and username vs email login

WordPress standard authentication accepts either:

username
or
email address

for the normal login identifier.

That means an attacker may target either representation of the same account.

For the implications of those identifiers, see WordPress Login: Username vs. Email, Which Is More Secure?.

Restricting the login identifier is another control

TheOneWP Restrict Login Identifier can control which supported identifier type is accepted during authentication.

That does not replace rate limiting.

The responsibilities are:

Restrict Login Identifier
→ which identifiers are accepted

Rate limiter
→ how repeated failures are controlled

Whitelisted IPs and rate limiting

Some systems allow known trusted networks to bypass ordinary attempt counters.

For example:

agency VPN IP
→ trusted

office static IP
→ trusted

all other sources
→ normal rate limiter

This can prevent legitimate internal testing or occasional password mistakes from triggering lockouts.

Whitelisting needs careful network identification

Never whitelist an address until you know WordPress is actually seeing the intended client address.

If a site sits behind a proxy and WordPress sees only the proxy IP, whitelisting that address could unintentionally exempt a much wider group of users.

Review WordPress IP Blacklist and Whitelist Guide before enabling trusted-IP exceptions.

TheOneWP Access Manager uses trusted-IP exemptions

TheOneWP Access Manager includes separate IPv4 and IPv6 whitelist controls.

In the current implementation, whitelisted addresses are exempt from:

  • failed-attempt counting;
  • temporary lockout checks.

The user still needs valid WordPress authentication credentials.

Blacklist checks remain separate

A manually blacklisted source should not simply be treated as:

rate limit already exceeded

The blacklist represents an explicit policy decision, while a temporary lockout represents dynamic behavior after failures.

Keeping them separate makes administration clearer.

What should happen during an active lockout?

A WordPress-level limiter can return a generic:

WP_Error

through the authentication pipeline.

For example:

Too many login attempts.
Please try again later.

Do not continue checking passwords during the active lockout if the whole purpose is to stop repeated credential verification.

HTTP 429 and rate limiting

At the HTTP layer, the conventional status for rate limiting is:

429 Too Many Requests

The MDN documentation for HTTP 429 defines it as a response indicating that a client has sent too many requests within a period.

A response can also include:

Retry-After

to tell a client when another request should be attempted.

The MDN Retry-After reference documents that header.

Do not force 429 into every WordPress login implementation

A WordPress plugin integrating through the standard login error flow may reasonably use a normal WP_Error response.

A WAF, reverse proxy or API-level rate limiter is more naturally positioned to return an explicit:

429 Too Many Requests

response.

Choose the response according to the layer performing the enforcement.

Temporary lockouts are usually preferable to permanent automatic bans

If five failed passwords immediately create a permanent IP blacklist entry, legitimate mistakes can have excessive consequences.

Temporary lockouts allow the security system to slow repeated attempts without requiring an administrator to manually repair every false positive.

Do not automatically blacklist every locked-out IP

An address might belong to:

  • a legitimate employee;
  • a shared office gateway;
  • a customer network;
  • a VPN service;
  • a mobile carrier.

Use the login log and wider evidence before escalating a temporary event into a long-lived deny rule.

Progressive lockouts reduce the need for permanent bans

Instead of:

5 failures
→ permanently blocked

you can use:

first threshold
→ short lockout

repeated threshold
→ longer lockout

continued repetition
→ longer again

This creates increasing friction for repeated automation while keeping recovery possible.

Limit the maximum progressive duration

An exponential formula grows quickly.

For example:

5
10
20
40
80
160
320 minutes

At some point the lockout may become operationally unreasonable.

Define a maximum duration.

What threshold should you use?

There is no universal WordPress number that is correct for every installation.

A useful policy depends on:

  • number of legitimate users;
  • user technical ability;
  • account sensitivity;
  • 2FA deployment;
  • traffic volume;
  • network architecture;
  • availability requirements.

A threshold such as:

5 failures

is a common example, not a WordPress Core recommendation that every site must copy.

Too-low thresholds create support problems

Consider:

maximum attempts:
2

lockout:
24 hours

A legitimate user with an outdated password manager could lock themselves out almost immediately.

Too-high thresholds provide little friction

At the other extreme:

maximum attempts:
500

may allow substantial automated guessing before any response occurs.

Choose a reasonable observation window

The counter should normally expire when attempts stop.

For example:

5 failures in 15 minutes
→ lockout

is different from:

5 failures across 6 months
→ lockout

The second policy can punish unrelated old mistakes.

Define threshold, window and duration together

These values cannot be evaluated independently.

For example:

3 failures
over 5 minutes
with 2-minute lockout

has a very different operational effect from:

3 failures
over 30 days
with 24-hour lockout

Do not leak the remaining threshold unnecessarily

A login screen does not necessarily need to tell an unauthenticated visitor:

You have exactly 2 attempts remaining.

That information can help automated clients tune their behavior.

A generic failed-login response is often sufficient.

Countdown messages are a UX decision

During an active lockout you may choose to show:

Try again in 8 minutes.

or simply:

Too many login attempts.
Please try again later.

The more detailed response can help legitimate users.

The more generic response reveals less state.

Choose deliberately.

Do not reveal whether the username exists

A rate limiter should avoid responses such as:

User admin is locked.

unless the context already safely assumes the identity is known.

For public authentication, generic responses reduce account-enumeration differences.

What happens after a successful login?

One design choice is whether a successful authentication clears previous failure counters.

For an IP-only limiter, clearing the complete IP counter after any successful user login can be problematic on shared networks.

For an account-and-source limiter, clearing the relevant account/source bucket can be more logical.

Define the behavior according to the key model you chose.

A successful login should still be logged

Consider:

15 failed attempts
↓
one successful login

The success may be operationally more important than all preceding failures.

Keep monitoring separate from the counter-reset decision.

TheOneWP login log records multiple event types

The current Access Manager combines rate limiting with authentication-event visibility so administrators can review successful, failed, blocked and locked activity.

Its login-event table is capped rather than growing without limit.

Login monitoring should have a retention policy

A rate limiter needs temporary state.

An audit log needs a separate retention decision.

Do not keep an unlimited authentication history simply because storage is available.

See How to Monitor WordPress Login Attempts.

Do not store raw credentials in rate-limit logs

Never store:

  • passwords;
  • authentication cookies;
  • 2FA codes;
  • password reset tokens.

A login-protection system should reduce credential risk, not create another credential database.

Two-factor authentication remains important

Rate limiting helps reduce guessing.

It does not solve:

attacker already knows
the correct password

A stolen or reused credential may succeed on the first attempt.

TheOneWP Two-Factor Authentication addresses that deeper problem by requiring another authentication factor.

Use 2FA for privileged accounts

Administrator and other sensitive accounts deserve stronger protection because compromise can expose:

  • plugin installation;
  • theme changes;
  • user administration;
  • site settings;
  • content;
  • customer data;
  • integrations.

The official Hardening WordPress guide recommends strong passwords and additional authentication protection as part of a layered security strategy.

Rate limiting cannot fix reused passwords

Credential stuffing uses credentials stolen from another service.

If the attacker already has:

correct email
+
correct reused password

the first login may succeed.

Use:

unique passwords
+
password manager
+
2FA

alongside rate limiting.

Changing the WordPress login URL is another layer

Moving away from the predictable default login endpoint can reduce automated noise aimed specifically at:

/wp-login.php

See How to Change the WordPress Login URL.

A custom login URL does not replace rate limiting

If the alternate endpoint is discovered, the authentication system still needs protection against repeated attempts.

This is why Does Hiding wp-login.php Actually Help Security? treats endpoint obscurity as a supporting control rather than the primary security boundary.

Block User Login solves a different problem

A rate limiter answers:

Has this source or account
attempted authentication
too many times?

TheOneWP Block User Login answers:

Should this user account
be allowed to authenticate at all?

Use account suspension when access should be revoked without deleting the WordPress user.

Rate limiting does not replace user cleanup

An old administrator account remains an unnecessary authentication target even if attempts against it are rate-limited.

Review unused accounts through Auditing Dormant WordPress User Accounts.

Last Login provides different information

TheOneWP Last Login records successful authentication context for users.

This can help distinguish:

active account
from
account unused for months

while the rate limiter focuses on repeated failed activity.

User-role audits matter after authentication

Rate limiting controls entry attempts.

It does not determine what a successfully authenticated user may do.

Review privileges with How to Audit User Roles on a WordPress Site.

Alternative authentication paths need attention

The visible WordPress login form is not necessarily the only way authentication occurs.

Depending on the installation, authentication may involve:

  • XML-RPC;
  • REST API authentication;
  • Application Passwords;
  • mobile applications;
  • custom forms;
  • single sign-on;
  • third-party authentication plugins.

Protecting wp-login.php alone may not protect every authentication path

A rate limiter attached only to:

login_form

or visual page rendering may never see another authentication flow.

Using the standard WordPress:

authenticate

pipeline gives application-level logic broader coverage when those authentication methods rely on normal WordPress authentication.

Do not claim universal coverage without testing

A custom SSO system can bypass parts of the normal WordPress username/password workflow.

Verify:

  • normal login;
  • XML-RPC if enabled;
  • REST authentication;
  • Application Passwords;
  • custom login forms;
  • SSO.

according to the site you are protecting.

XML-RPC deserves particular attention

The official WordPress brute-force documentation notes that XML-RPC can become another authentication target.

If XML-RPC is not needed, consider whether it should remain enabled.

If it is needed, ensure the security architecture accounts for its authentication traffic.

Application Passwords are separate credentials

Application Passwords are designed for programmatic WordPress access.

Do not accidentally break legitimate API integrations by applying an interactive login rule without testing those workflows.

Login attempt limiting behind Cloudflare or another proxy

IP-based rate limiting depends on a reliable source address.

If the server sees the CDN edge instead of the visitor, the counter can group unrelated users together.

For example:

Visitor A
Visitor B
Visitor C
↓
same proxy address
↓
same rate-limit bucket

This can produce large false-positive lockouts.

Fix client-IP restoration at the infrastructure layer

A robust proxy setup should ensure the origin receives a trustworthy client address through its configured server mechanism.

Do not make a WordPress plugin guess which forwarded header happens to contain the correct value.

TheOneWP Access Manager uses REMOTE_ADDR

The current Access Manager implementation deliberately uses:

REMOTE_ADDR

for source-based access rules and rate limiting rather than automatically trusting forwarding headers.

If the site sits behind a CDN, reverse proxy or load balancer, verify which address appears there before enabling the limiter.

Whitelisting a proxy IP can be dangerous

If every visitor appears to WordPress as the same proxy address and that address is added to the whitelist, the ordinary limiter could effectively be bypassed for all those visitors.

Verify the production request path first.

Locking a proxy IP can be equally bad

If the same proxy address represents many users, a few failures can lock out everyone routed through that edge address.

This is another reason network configuration is part of application security.

What HTTP status should edge rate limiting return?

For generic HTTP rate limiting, the standard status is:

429 Too Many Requests

It indicates that the client has sent too many requests within a given period.

A WAF or reverse proxy can also provide:

Retry-After

to indicate when another request may be appropriate.

Do not use 503 for ordinary client-specific rate limiting

503 Service Unavailable indicates that the server is temporarily unable to handle the request, such as during maintenance or overload.

For a client exceeding a rate limit, 429 communicates the condition more accurately.

CAPTCHA is another supporting control

A CAPTCHA or bot challenge can increase the cost of automation.

OWASP recommends treating CAPTCHA as defense in depth rather than a complete brute-force prevention mechanism.

A common model is:

normal login
↓
several failures
↓
additional challenge

rather than forcing every legitimate user through a challenge from the first request.

Do not use CAPTCHA as the only defense

CAPTCHA systems can:

  • be solved;
  • be outsourced;
  • create accessibility concerns;
  • add third-party dependencies;
  • increase login friction.

Use them as one additional control where appropriate.

What does TheOneWP Access Manager do?

TheOneWP Access Manager combines several related login-access controls in one module:

failed-attempt threshold
+
base lockout duration
+
optional progressive lockouts
+
IPv4 / IPv6 blacklist
+
IPv4 / IPv6 whitelist
+
authentication-event log

Configurable maximum attempts

The current implementation lets administrators define the maximum number of failed attempts before a temporary lockout begins.

The saved value is normalized as a positive integer and cannot be configured below one.

Configurable lockout duration

The lockout duration is configured in minutes.

That lets the policy match the site’s operational needs rather than forcing one hardcoded duration onto every installation.

Progressive lockout mode

When progressive lockout is enabled, repeated lockout cycles increase the transient duration according to the stored lockout round.

This creates a gradually stronger response to repeated abuse without automatically converting the source into a permanent blacklist entry.

Whitelisted sources bypass rate-limiter checks

Trusted IP addresses configured in the Access Manager whitelist are exempt from the failed-attempt counter and temporary lockout checks.

The user still needs valid authentication credentials.

Blacklisted addresses remain a separate access rule

Blacklist checks are not equivalent to temporary lockouts.

The normal login page evaluates the blacklist separately, which keeps deliberate deny rules distinct from automatic rate-limit state.

Authentication events are logged separately

The module records authentication-event information so administrators can review patterns rather than operating the limiter blindly.

Login-event IP values are stored as salted SHA-256 hashes rather than directly readable source addresses.

The Access Manager event log is capped

The current implementation keeps at most:

500 rows

and removes older excess entries as new events are inserted.

This prevents the login-event table from growing without limit.

Rate limiting configuration is sensitive

The current Access Manager administration interface requires:

manage_options

for access and protected AJAX operations also require the module nonce.

A login-security configuration should not be editable by ordinary content users.

Testing a WordPress login limiter

Do not enable a new lockout policy on production and simply assume it works.

Use a controlled test account and a recovery path.

Test 1: one failed login

Enter one intentionally incorrect password.

Confirm:

authentication fails
+
counter increases
+
no premature lockout

Test 2: threshold

Continue controlled failures until the configured threshold is reached.

Confirm:

lockout activates
at expected attempt

Test 3: active lockout

Try again during the lockout.

Confirm that credential verification does not proceed normally and the intended lockout response appears.

Test 4: expiry

Wait for the configured duration in a development environment.

Confirm that authentication becomes available again.

Test 5: correct credentials after expiry

Confirm a legitimate user can authenticate normally once the lockout ends.

Test 6: whitelist

If trusted-IP exemptions exist, verify:

whitelisted source
→ ordinary lockout bypass

but
→ credentials still required

Test 7: proxy environment

Inspect the source address on the actual hosted environment.

Do not rely only on local development.

Test 8: IPv6

If the infrastructure supports IPv6, test it explicitly rather than assuming the IPv4 path proves everything.

Keep a recovery path before testing

Before intentionally triggering a lockout, make sure you can recover through at least one alternative:

  • hosting panel;
  • SSH;
  • SFTP;
  • database management;
  • WP-CLI;
  • another trusted network;
  • another administrator session.

Do not test with your only Administrator access

This combination is unnecessarily risky:

one admin account
+
one connection
+
aggressive lockout
+
no hosting access

Use a dedicated test account.

Test legitimate password mistakes

A security policy should tolerate normal human behavior.

Try:

wrong password once
wrong password twice
correct password

and verify that the experience remains reasonable.

Test password managers

A saved old password can automatically generate several failures if a browser or password manager retries it.

Make sure the policy does not create excessive lockouts from ordinary credential updates.

Test shared networks

If multiple employees authenticate from one office public IP, an IP-only limit affects them as a group.

Include that scenario in policy design.

Test mobile users

Mobile users can move between:

  • Wi-Fi;
  • cellular data;
  • different carrier gateways;
  • IPv4;
  • IPv6.

Do not assume one user always has one source address.

Monitor false positives after deployment

After enabling the limiter, review:

  • how often lockouts occur;
  • whether legitimate users report trouble;
  • which accounts are targeted;
  • whether traffic is distributed across many IPs;
  • whether whitelists behave correctly;
  • whether 2FA-protected accounts are still heavily targeted.

Adjust policy based on evidence

Do not choose:

3 attempts
+
24-hour lockout

because one generic hardening article used those numbers.

Measure the actual site.

Common mistake: no observation window

A counter that never resets can eventually lock out a legitimate source after unrelated mistakes spread across months.

Use an expiration window.

Common mistake: no lockout expiration

A temporary limiter should have a defined recovery path.

Do not accidentally create permanent denial through data that never expires.

Common mistake: one extremely aggressive threshold

An attacker should not be able to lock out an account indefinitely with a handful of requests.

Consider the denial-of-service consequences of the policy.

Common mistake: IP-only protection against distributed attacks

An attacker using hundreds of addresses can remain below every per-IP threshold.

Use monitoring and additional controls.

Common mistake: account-only permanent lockouts

An attacker who knows the username can deliberately lock the victim out repeatedly.

Use temporary and recoverable policies.

Common mistake: trusting proxy headers automatically

A security counter is only as useful as the source identity used to build its key.

Validate the proxy architecture first.

Common mistake: whitelisting the CDN edge

If many users appear through that address, the limiter can be bypassed far more broadly than intended.

Common mistake: blocking the CDN edge

The same configuration mistake can lock out large numbers of legitimate users.

Common mistake: logging passwords

Never include credentials in rate-limit diagnostics.

Common mistake: resetting all counters after any successful login

On a shared-IP implementation, one successful user can unintentionally clear state associated with other activity.

Reset only the state your key model actually associates with that successful authentication.

Common mistake: permanent IP bans after one lockout

Temporary abuse-control state and administrator-defined blacklist policy should remain separate.

Common mistake: assuming a custom login URL replaces throttling

It does not.

Once the custom endpoint is discovered, repeated attempts remain possible.

Common mistake: assuming 2FA makes rate limiting unnecessary

2FA protects the account even when a password is known, but unlimited automated password traffic can still:

  • consume resources;
  • generate noise;
  • target users without 2FA;
  • stress authentication systems.

Common mistake: assuming rate limiting makes 2FA unnecessary

A correct stolen password may work on the first attempt.

Rate limiting cannot help much in that scenario.

Common mistake: protecting only the visual login page

Test other authentication surfaces that matter to the site.

Common mistake: rate limiting every site request

A login limiter should target authentication traffic.

Do not accidentally throttle normal frontend browsing because several visitors share an IP.

Common mistake: keeping no logs

Without visibility, administrators cannot tell whether:

  • lockouts are working;
  • legitimate users are affected;
  • attacks are distributed;
  • specific accounts are targeted.

Common mistake: retaining unlimited logs

Define retention and cleanup separately from rate-limit state.

Common mistake: exposing detailed lockout information publicly

Keep public authentication responses generic enough to avoid unnecessary account-state disclosure.

Common mistake: forgetting dormant accounts

Rate limiting a login attempt against an unnecessary Administrator account still leaves an unnecessary Administrator account.

Review account lifecycle directly.

A practical layered login configuration

A balanced WordPress login-security architecture might look like:

WAF / CDN
→ high-volume request limiting

Custom login URL
→ reduce default-endpoint noise

WordPress rate limiter
→ authentication-aware lockouts

IP blacklist / whitelist
→ explicit network policies

Strong passwords
→ credential strength

2FA
→ second authentication factor

Block User Login
→ account suspension

Login log
→ operational visibility

Role audit
→ least privilege

No single layer is expected to solve every threat.

How TheOneWP modules fit together

Access Manager handles failed-login limits, lockouts, IP policies and authentication-event visibility.

Two-Factor Authentication adds another factor for supported accounts.

Block User Login prevents selected accounts from authenticating without deleting them.

Restrict Login Identifier controls which standard identifier types can be used for login.

Last Login records successful authentication activity for account auditing.

These controls address different stages:

Restrict Login Identifier
→ accepted identity input

Access Manager
→ repeated-attempt control

Two-Factor Authentication
→ additional proof

Block User Login
→ account eligibility

Last Login
→ successful access context

WordPress login-attempt limiting checklist

  • Define the maximum failed-attempt threshold.
  • Define an observation window.
  • Define a temporary lockout duration.
  • Decide whether progressive lockouts are appropriate.
  • Set a sensible maximum progressive duration.
  • Decide whether counters are IP-based, account-based or multi-signal.
  • Understand the denial-of-service tradeoffs of account lockouts.
  • Understand the distributed-attack limitations of IP-only lockouts.
  • Verify the source IP when using IP-based rules.
  • Do not blindly trust forwarding headers.
  • Verify CDN and reverse-proxy configuration.
  • Support IPv6 where the infrastructure uses it.
  • Use wp_login_failed to react to failed standard authentication.
  • Use authenticate when blocking the standard authentication pipeline.
  • Never log submitted passwords.
  • Use temporary storage with explicit expirations for temporary counters.
  • Keep permanent blacklists separate from temporary lockouts.
  • Use generic public lockout messages where practical.
  • Do not reveal unnecessary account-existence information.
  • Consider HTTP 429 at the edge or server rate-limiting layer.
  • Keep authentication logs separate from counters.
  • Define login-log retention.
  • Test one failed login.
  • Test the exact threshold.
  • Test the active lockout.
  • Test automatic expiry.
  • Test successful login after expiry.
  • Test whitelisted sources where supported.
  • Test shared-network scenarios.
  • Test mobile and changing-network scenarios.
  • Test proxy behavior in production-like infrastructure.
  • Test XML-RPC where enabled and in scope.
  • Test API authentication paths where relevant.
  • Test SSO and custom authentication separately.
  • Keep a recovery path before aggressive testing.
  • Use strong, unique passwords.
  • Enable 2FA for sensitive accounts.
  • Review dormant users.
  • Audit privileged roles.
  • Monitor successful as well as failed authentication.
  • Use edge protection for high-volume attacks.
  • Review the policy after infrastructure changes.

Related guides

Final recommendation

Limiting WordPress login attempts should slow repeated authentication abuse without turning ordinary password mistakes into unnecessary administrator lockouts.

Start with three explicit values:

attempt threshold
+
observation window
+
lockout duration

Then decide which signal owns the counter.

IP-based limiting is useful for repeated traffic from one network source, but distributed attacks can rotate addresses and shared networks can create false positives.

Account-based limiting can detect distributed targeting, but aggressive account lockouts can be abused to deny legitimate users access.

Higher-risk systems may therefore use multiple signals rather than relying entirely on one identifier.

At the WordPress application layer, the architecture can use:

wp_login_failed
→ increment failed-attempt state

authenticate
→ reject active lockouts

Transients API
→ temporary counters and expiry

Never store or log the submitted password while implementing those controls.

For heavy automated traffic, move part of the enforcement closer to the network edge so abusive requests can be rejected before WordPress and PHP consume unnecessary resources.

Finally, treat rate limiting as one layer rather than the entire login-security system:

rate limiting
+
strong unique passwords
+
two-factor authentication
+
IP access rules
+
login monitoring
+
account lifecycle management
+
least privilege

TheOneWP Access Manager implements the login-attempt layer with configurable failed-attempt thresholds, timed lockouts, optional progressive lockouts, IPv4 and IPv6 blacklist and whitelist controls and authentication-event visibility.

Use the limiter to slow repeated failures, use the whitelist only for verified trusted sources, use the blacklist for explicit deny rules and use monitoring to verify whether the policy is helping real traffic rather than merely accumulating settings.

Simplify your WordPress stack

A modular WordPress toolkit. 104 focused tools.

Ultimately, you can build cleaner workflows, maintain fewer plugins and enable only the features each website actually needs.