
If you tail the access log of any public WordPress site for an afternoon, you will see it: a steady drip of POST requests to wp-login.php and xmlrpc.php from IP addresses scattered across the world. This is brute forcing, and it is not personal. Botnets work down lists of millions of WordPress domains, trying leaked and common credentials against each one. The good news is that brute force is one of the few WordPress threats with a clean, complete solution. The catch is that "install a security plugin" is only a third of the answer, and the part most people get wrong is where the limit is actually enforced.
The phrase covers three distinct behaviors, and conflating them is why so many sites are "protected" yet still get popped.
A defense that only counts failures per IP per minute handles the first case beautifully and the other two poorly. You need layers that account for all three.
Everyone fixates on wp-login.php. Attackers do not. WordPress exposes three authentication entry points, and protecting only the front door leaves two windows open.
This is the form-based login. It is the most visible target and, ironically, the best defended out of the box once you add any reputable plugin. It is also the only door most "limit login attempts" tools watch.
The legacy XML-RPC endpoint is the one that quietly ruins sites. Its system.multicall method lets a client batch many calls into a single HTTP request. An attacker can stuff hundreds of wp.getUsersBlogs credential checks into one POST. To a naive rate limiter counting requests, that is a single hit; to your site, it is hundreds of password attempts. If you do not use the Jetpack, the WordPress mobile app, or pingbacks, the cleanest fix is to block the endpoint entirely. Most modern security plugins offer a one-click toggle, or you can deny it at the web server. Do not half-disable it — turning off some methods while leaving multicall reachable is the worst of both worlds.
Since WordPress 5.6, Application Passwords let external tools authenticate to the REST API with a per-application credential. They are genuinely useful, but they are also a brute force surface that lives at /wp-json/ and, critically, they do not honor your two-factor plugin. A correct application password authenticates straight through. If you do not use headless integrations or external publishing tools, disable Application Passwords with a filter (add_filter('wp_is_application_passwords_available', '__return_false')) or via a security plugin. If you do use them, treat each one like a server credential and revoke them the moment an integration is retired.
This is the part the typical listicle skips. The same rule has wildly different effectiveness depending on which layer applies it.
A rule at Cloudflare, your CDN, or a managed host's WAF stops the request before it ever reaches PHP. This is the only layer that meaningfully blunts distributed and amplification attacks, because it can rate-limit by request to wp-login.php and xmlrpc.php across all traffic, challenge suspicious clients, and absorb the load instead of your origin server burning CPU on each rejected attempt. Cloudflare's free tier plus a couple of custom rate-limiting rules covers most small sites; managed hosts like Kinsta, WP Engine, and Cloudways ship equivalent protection at the platform edge.
Plugins such as Wordfence, Solid Security (formerly iThemes), and the well-maintained Limit Login Attempts Reloaded run inside WordPress. They give you the richest controls — username-aware lockouts, login logs, country blocking — but they only act after PHP and the database have already done work to serve the request. Against a flood, the plugin can be enforcing limits perfectly while the server falls over from the request volume. Application-layer tools are for visibility and precision, not for absorbing scale.
fail2ban, watching your auth logs and dropping offending IPs at the firewall, sits between the two. It is excellent on a VPS you control and irrelevant on most managed hosting where you do not own the firewall. Know which situation you are in before you reach for it.
The practical takeaway: put the rate limit at the edge, put the intelligence in the application. Relying solely on a plugin is the single most common mistake.
Here is a failure mode that silently disables protection on a lot of sites. When WordPress sits behind a CDN or reverse proxy, every request arrives from the proxy's IP. Your plugin must read the real client IP from a forwarded header (X-Forwarded-For, CF-Connecting-IP) instead. Two things go wrong:
X-Forwarded-For on every request, rotating the spoofed value to dodge every per-IP rule.Wordfence and Solid Security both have an explicit "How does this site get visitor IP addresses?" setting. Get it right for your stack and test it — a misconfigured value here is why a "protected" site still logs unlimited attempts.
Rate limiting buys time and cuts noise. Two measures end the underlying risk:
Layer on a CAPTCHA or Cloudflare Turnstile challenge at the login form for bot friction, and rename or hide the login URL if you want to shed the bulk of dumb automated scans — useful, but treat both as noise reduction, not real authentication. The serious protection is unique passwords plus 2FA, enforced behind an edge rate limit, with XML-RPC and Application Passwords shut off if you do not use them.
wp-login.php and xmlrpc.php (Cloudflare rules or your host's WAF).admin.None of this stops vulnerability exploits in outdated plugins — that is a separate fight on a different layer. But for brute force, credential stuffing, and the XML-RPC amplification that quietly takes sites down, the above is a genuinely complete answer. The reason sites still fall is almost never that the countermeasures failed. It is that someone protected one door and left the other two open.
Site
Tools
We do not sell your email. We do not spam.
© 2026 RevealTheme. All rights reserved.