RevealTheme logo
Back to Blog

WordPress Brute Force Attacks: What Stops Them

WordPress Brute Force Attacks: What Stops Them
The RevealTheme Team

By

·

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.

What "brute force" actually means in 2026

The phrase covers three distinct behaviors, and conflating them is why so many sites are "protected" yet still get popped.

  • Classic password guessing. A bot hammers the login form with username/password pairs. Slow, noisy, easy to spot, and the easiest to stop.
  • Credential stuffing. The bot already has a real password — yours, harvested from a breach of some unrelated service — and is testing whether you reused it. There is no "guessing" to rate-limit away; often a single attempt per account succeeds. This is now the dominant variant, and ordinary lockout rules barely touch it.
  • Distributed low-and-slow attacks. Thousands of compromised IPs each make a handful of attempts, staying under any per-IP threshold. The aggregate is a brute force; each individual source looks innocent.

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.

The three doors, not one

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.

1. wp-login.php — the front door

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.

2. xmlrpc.php — the amplifier

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.

3. The REST API and Application Passwords — the bypass

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.

Where you enforce the limit decides whether it works

This is the part the typical listicle skips. The same rule has wildly different effectiveness depending on which layer applies it.

Edge / network layer

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.

Application layer

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.

Server layer

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.

The X-Forwarded-For trap

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:

  • If the plugin is configured to read the connection IP, it sees one shared proxy address for everyone. One attacker triggers a lockout that blocks every legitimate visitor — or, worse, the plugin trusts a header an attacker can spoof and the rate limit becomes meaningless.
  • If you trust a forwarded header without restricting it to your actual CDN's ranges, an attacker simply sends a fake 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.

The two controls that actually end the threat

Rate limiting buys time and cuts noise. Two measures end the underlying risk:

  1. Unique, long, random passwords — managed, never reused. Credential stuffing only works because a password from another breach matches the one here. A unique 16+ character password from a password manager makes both stuffing and guessing irrelevant. This single habit defeats the variant rate limits cannot.
  2. Two-factor authentication on every admin and editor account. Even a correctly stuffed password fails without the second factor. Use a TOTP authenticator app rather than SMS, which is phishable and SIM-swappable. The setup costs five minutes per user. Just remember it does not cover the Application Password / REST path — close that door separately.

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.

A ten-minute hardening pass

  • Turn on edge rate limiting for wp-login.php and xmlrpc.php (Cloudflare rules or your host's WAF).
  • Disable XML-RPC entirely unless a tool genuinely needs it.
  • Disable Application Passwords unless you run a headless or external-publishing integration; audit and revoke any you find.
  • Install one application-layer plugin for logging and lockouts, and verify its visitor-IP setting matches your CDN.
  • Move every account to a unique password-manager credential and enable TOTP 2FA.
  • Delete or rename any account literally named 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.