
How to limit login attempts in WordPress?
- Sajjad
- WordPress, Security
- 07 Sep, 2026
To limit login attempts in WordPress, install a dedicated plugin such as Limit Login Attempts Reloaded or Loginizer, set a maximum number of failed logins (for example, 4 or 5), and choose how long an offending IP address should be locked out. If you prefer not to add a plugin, you can achieve the same result with a small custom plugin that uses the authenticate filter and the wp_login_failed action, or by rate limiting wp-login.php at the web server level.
Out of the box, WordPress lets anyone try as many username and password combinations as they like, as fast as their connection allows. That is exactly what automated brute-force bots rely on. This article explains why limiting login attempts matters, how to set it up with popular plugins, how to build a lightweight version yourself, how to add server-level protection, and how to avoid locking yourself out in the process.
Why Should You Limit Login Attempts?
A brute-force attack is simple: a bot sends thousands of login requests, each with a different password, hoping one of them works. A variation called credential stuffing uses username and password pairs leaked from other sites, betting that people reuse passwords. Neither attack is clever, but both work surprisingly often against sites with no protection.
Limiting login attempts helps you in several ways:
- It slows attackers down dramatically: Instead of thousands of guesses per hour, a bot gets a handful before it is locked out, which makes guessing a strong password practically impossible.
- It reduces server load: Every login request runs PHP and queries the database. A sustained attack can slow down a small server or even take it offline.
- It gives you visibility: Most lockout plugins log blocked IPs and usernames, so you can see who is targeting your site and which usernames they are trying.
- It buys time for other defences: Even if a weak password exists somewhere on your site, a lockout policy makes it far less likely to be found before you fix it.
Keep in mind that limiting login attempts is one layer, not a complete solution. It works best alongside strong passwords, two-factor authentication, and keeping WordPress, themes, and plugins updated.
How WordPress Handles Logins by Default
When someone submits the login form at wp-login.php, WordPress calls wp_signon(), which in turn calls wp_authenticate(). That function runs the authenticate filter, where WordPress core checks the username or email and password. If the check fails, WordPress fires the wp_login_failed action. If it succeeds, WordPress fires wp_login and sets the authentication cookies.
The important part is that nothing in this process counts failures or blocks repeat offenders. Core simply returns an error message and waits for the next attempt. Every lockout plugin, and the custom code later in this article, hooks into these same points to add the missing counting and blocking logic.
It is also worth knowing that wp-login.php is not the only place people can try passwords. The legacy XML-RPC endpoint (xmlrpc.php) also accepts usernames and passwords, and it can be abused to test many passwords in a single request. A good lockout solution should cover XML-RPC as well, or you should disable XML-RPC if you don't use it.
Method 1: Limit Login Attempts With a Plugin
For most site owners, a plugin is the quickest and safest option. Several well-known plugins handle this well.
Limit Login Attempts Reloaded
Limit Login Attempts Reloaded is one of the most widely installed plugins for this specific job. It is lightweight and focuses on doing one thing well.
-
Install the plugin: Go to Plugins > Add New Plugin, search for "Limit Login Attempts Reloaded", then click Install Now and Activate.
-
Open the settings: Navigate to the plugin's menu in the admin sidebar (it appears as Limit Login Attempts), then open the Settings tab.
-
Set allowed retries: Choose how many failed attempts are allowed before a lockout. Values between 3 and 5 are a sensible balance between security and forgiving genuine typos.
-
Set lockout duration: Choose how long the first lockout lasts, such as 20 minutes.
-
Configure escalating lockouts: Set how many lockouts trigger a longer ban (for example, after 4 lockouts, block for 24 hours). This punishes persistent bots without harming someone who mistypes their password twice.
-
Add your IP to the safelist: If you have a static IP address, add it to the safelist so you can never lock yourself out.
-
Save your changes: Click Save Settings and test the login form from a private browser window.
The plugin also offers an optional cloud service with extra features, but the free local mode is enough for most sites.
Loginizer
Loginizer is another popular option with a similar feature set. After installing and activating it from Plugins > Add New Plugin, go to Loginizer Security > Brute Force. There you can set the maximum retries, lockout time, extended lockout, and how many lockouts trigger the extended one. You can also add IPs to a whitelist or blacklist.
Security Suites With Built-In Lockouts
If you already use a full security plugin, you probably don't need a separate lockout plugin. Running two plugins that both count failed logins can cause confusing behaviour.
- Wordfence: Under Wordfence > All Options > Brute Force Protection, you can set how many failures trigger a lockout, the counting window, and the lockout duration. It also lets you immediately lock out anyone who tries an invalid username.
- Solid Security (formerly iThemes Security): Includes local brute force protection with settings for maximum login attempts per host and per user, plus a lockout period.
- All-In-One Security (AIOS): Offers a login lockout feature with configurable retries, lockout length, and email notifications.
- Jetpack: Includes brute force protection that uses a shared network of known malicious IPs, which is handy if you already rely on Jetpack.
Pick one and configure it well rather than stacking several.
Recommended Settings
Settings vary by site, but these are reasonable starting points:
- Allowed retries: 4 to 5.
- First lockout: 15 to 30 minutes.
- Extended lockout: 12 to 24 hours after 3 or 4 lockouts.
- Reset counter: After 12 to 24 hours without failures.
- Notifications: Email alerts only for extended lockouts, or you'll be flooded on busy sites.
Method 2: Build a Lightweight Lockout Plugin
If you like to keep your plugin list short, or you want to understand exactly what's happening, you can write a small plugin that counts failures per IP address and blocks further attempts once a limit is reached. It uses WordPress transients, which expire automatically, so there's no cleanup required.
Create a new folder at wp-content/plugins/sajjad-login-limiter/ and add a file called sajjad-login-limiter.php with the following code:
<?php
/**
* Plugin Name: Sajjad Login Limiter
* Description: Locks out an IP address after too many failed login attempts.
* Version: 1.0.0
* Author: Sajjad
*/
defined( 'ABSPATH' ) || exit;
define( 'SAJJAD_LL_MAX_ATTEMPTS', 5 );
define( 'SAJJAD_LL_LOCKOUT_SECONDS', 15 * MINUTE_IN_SECONDS );
/**
* Get the visitor's IP address.
*
* Uses REMOTE_ADDR only, because headers like X-Forwarded-For
* can be spoofed by the client.
*/
function sajjad_ll_get_ip() {
$ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : '0.0.0.0';
}
/**
* Build the transient key for an IP address.
*/
function sajjad_ll_key( $ip ) {
return 'sajjad_ll_' . md5( $ip );
}
/**
* Block authentication if the IP is currently locked out.
*
* Runs at priority 30, after core's username/password check (priority 20),
* so it overrides even a correct password while the lockout is active.
*/
function sajjad_ll_check_lockout( $user, $username, $password ) {
if ( empty( $username ) && empty( $password ) ) {
return $user;
}
$attempts = (int) get_transient( sajjad_ll_key( sajjad_ll_get_ip() ) );
if ( $attempts >= SAJJAD_LL_MAX_ATTEMPTS ) {
$minutes = (int) ceil( SAJJAD_LL_LOCKOUT_SECONDS / MINUTE_IN_SECONDS );
return new WP_Error(
'sajjad_ll_locked',
sprintf(
/* translators: %d: number of minutes. */
esc_html__( 'Too many failed login attempts. Please try again in %d minutes.', 'sajjad-login-limiter' ),
$minutes
)
);
}
return $user;
}
add_filter( 'authenticate', 'sajjad_ll_check_lockout', 30, 3 );
/**
* Count a failed login for the current IP.
*/
function sajjad_ll_record_failure( $username ) {
$key = sajjad_ll_key( sajjad_ll_get_ip() );
$attempts = (int) get_transient( $key );
set_transient( $key, $attempts + 1, SAJJAD_LL_LOCKOUT_SECONDS );
}
add_action( 'wp_login_failed', 'sajjad_ll_record_failure' );
/**
* Reset the counter after a successful login.
*/
function sajjad_ll_clear_attempts( $user_login, $user ) {
delete_transient( sajjad_ll_key( sajjad_ll_get_ip() ) );
}
add_action( 'wp_login', 'sajjad_ll_clear_attempts', 10, 2 );
Activate it under Plugins > Installed Plugins, then test it from a private browser window by entering a wrong password five times. On the sixth attempt, even the correct password should be refused until the lockout expires.
How the Code Works
-
Counting failures: Each time
wp_login_failedfires, the plugin increments a counter stored in a transient keyed to the visitor's IP address. -
Blocking attempts: The
authenticatefilter runs on every login attempt. If the counter has reached the limit, it returns aWP_Error, which stops the login regardless of whether the password was correct. -
Expiring lockouts: The transient expires after the lockout period. Because each failed attempt resets the expiry, a bot that keeps hammering the login form stays locked out.
-
Clearing on success: A successful login deletes the counter so a legitimate user who mistyped once or twice starts fresh.
Because XML-RPC logins also pass through wp_authenticate(), this code covers xmlrpc.php password guessing as well.
Limitations to Be Aware Of
This lightweight approach is deliberately simple, so keep these points in mind:
- Proxies and CDNs: If your site sits behind Cloudflare or a load balancer,
REMOTE_ADDRmay be the proxy's IP, not the visitor's. In that case every visitor shares one counter. Configure your server to restore the real client IP (for example, with Apache'smod_remoteipor Nginx'sreal_ipmodule) rather than trustingX-Forwarded-Forin PHP. - Distributed attacks: Large botnets rotate through thousands of IPs. Per-IP limits still help, but a per-username limit or two-factor authentication adds a stronger barrier.
- Object caching: If you use a persistent object cache like Redis, transients are stored there instead of the database. That is fine, but flushing the cache also clears active lockouts.
- No logging or dashboard: A full plugin gives you logs and a safelist. Add those yourself if you need them.
Method 3: Rate Limit the Login Page at the Server Level
Plugins run inside WordPress, which means PHP still has to start and load WordPress for every blocked request. Rate limiting at the web server stops excess requests before PHP ever runs, which is more efficient under heavy attack. It works well together with a plugin rather than replacing it.
Nginx
Nginx has a built-in limit_req module. First, define a rate limiting zone in the http block, usually in /etc/nginx/nginx.conf:
# Allow roughly 6 requests per minute per IP to the login page.
limit_req_zone $binary_remote_addr zone=wplogin:10m rate=6r/m;
Then add a dedicated location for wp-login.php inside your site's server block. It needs its own PHP handling because an exact-match location takes priority over your general PHP location:
location = /wp-login.php {
limit_req zone=wplogin burst=3 nodelay;
limit_req_status 429;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
Adjust the fastcgi_pass socket path to match your PHP version and setup (check your existing PHP location block). Then test and reload:
sudo nginx -t && sudo systemctl reload nginx
With this in place, a visitor can make a short burst of requests, but anything faster than the defined rate gets an HTTP 429 response without touching PHP.
Apache and Managed Hosting
Apache has no simple built-in equivalent to limit_req. Modules like mod_evasive or a web application firewall such as ModSecurity can help, but they require server access and careful tuning. On shared or managed WordPress hosting, check whether your host already rate limits the login page, since many do. If you use Cloudflare, its rate limiting rules can target the /wp-login.php path at the edge.
Fail2Ban
On a VPS, Fail2Ban can watch your logs and ban IPs at the firewall after repeated failed logins. WordPress doesn't write failed logins to the server log by default, so you would typically pair Fail2Ban with a plugin that logs failures or with a filter that matches repeated POST requests to wp-login.php in your access log. That setup deserves its own guide, but it's a strong option for self-managed servers.
How to Avoid Locking Yourself Out
Lockouts are only a nuisance for attackers if they don't also catch you. A few habits prevent most problems:
- Use a password manager: Autofilled passwords don't get mistyped.
- Safelist your IP: If your office or home IP is static, add it to your plugin's safelist.
- Keep lockouts short at first: Start with a 15 to 20 minute lockout and increase it once you're confident the setup works.
- Tell your team: Editors and authors should know that repeated failed logins will temporarily block them.
What to Do If You Are Locked Out
If you do get locked out, you have a few ways back in:
-
Wait it out: The simplest fix is to wait for the lockout period to expire.
-
Use a different network: Switching from Wi-Fi to mobile data gives you a new IP address, which isn't locked out.
-
Deactivate the plugin via SFTP: Connect with SFTP or your host's file manager, go to
wp-content/plugins/, and rename the plugin's folder (for example, tolimit-login-attempts-reloaded-off). WordPress will deactivate it, and you can log in. Rename it back afterwards and reactivate it. -
Use WP-CLI: If you have SSH access, deactivate the plugin from the command line:
wp plugin deactivate limit-login-attempts-reloaded
For the custom plugin in this article, you can clear all transients, including active lockouts, with wp transient delete --all.
Best Practices for Login Protection
Limiting attempts is most effective when combined with a few other simple measures:
- Enforce strong, unique passwords: A lockout is far more effective when the password it protects can't be guessed in a handful of tries.
- Enable two-factor authentication: Even if a password leaks, a second factor stops the login.
- Don't use obvious usernames: Avoid
adminor your domain name as the administrator username. - Disable XML-RPC if you don't need it: It removes a second login path that attackers like to use.
- Monitor your logs: Review lockout logs occasionally to spot patterns, such as a specific username being targeted.
- Keep one lockout mechanism: Use either a dedicated plugin or your security suite's feature, not both, to avoid conflicts.
FAQ: Limiting Login Attempts in WordPress
No. WordPress core allows unlimited login attempts. You need a plugin, custom code, or server-level rate limiting to count failures and block repeat offenders.
Between 3 and 5 attempts is a common choice. It gives genuine users room for a typo or two while making brute-force guessing impractical.
Yes, if someone repeatedly enters the wrong password, or if many users share one IP address, such as an office network. Keep lockout periods short, safelist trusted IPs, and make sure your site sees real visitor IPs if you're behind a proxy.
Usually not. Both include brute force protection with configurable lockouts. Running a second lockout plugin on top can cause conflicts and confusing behaviour.
It depends on the tool. Many security plugins cover XML-RPC logins, and custom code hooked into the authenticate filter covers it too. If you don't use XML-RPC, disabling it is the simplest option.
It is a strong first step, but distributed botnets can rotate IP addresses. Combine it with strong passwords, two-factor authentication, and server-level rate limiting for the best protection.
Wait for the lockout to expire, switch to a different network, or rename the plugin's folder in wp-content/plugins using SFTP to deactivate it temporarily. With SSH access, you can also deactivate it using WP-CLI.
Conclusion
Limiting login attempts is one of the easiest and most effective ways to shut down brute-force attacks against your WordPress site. A plugin like Limit Login Attempts Reloaded or Loginizer takes only a few minutes to set up, security suites like Wordfence and Solid Security include the feature already, and developers can add a lean custom version using the authenticate filter and transients. On your own server, rate limiting wp-login.php in Nginx stops excess requests before WordPress even loads.
Whichever method you choose, test it carefully, safelist your own IP where possible, and know how to recover if you lock yourself out. Pair your lockout policy with strong passwords and two-factor authentication, and your login page goes from an open door to one of the hardest parts of your site to break into.


