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_failedto react to failed standard authentication. - Use
authenticatewhen 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
- WordPress Brute-Force Attacks, Explained
- How to Monitor WordPress Login Attempts
- WordPress IP Blacklist and Whitelist Guide
- WordPress Login Security Layers, Explained
- A WordPress Login Hardening Checklist
- WordPress Login Hooks Explained
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.

