Brute-Force Attacks on WordPress: How They Work, Stop Them
Diagnosis

Brute-Force Attacks on WordPress: How They Work, Stop Them

If your WordPress access logs are full of POST requests to /wp-login.php from a hundred different IPs, you're being brute-forced. It's the most common attack against WordPress sites by volume, it almost never targets you specifically, and it's defeated by a small number of well-chosen controls. This article explains the modern attack pattern, ranks the defenses by real-world impact, and names the protections that look reassuring but don't move the needle.

What a brute-force attack on WordPress actually is

A brute-force attack is exactly what the name suggests: an attacker tries many username-and-password combinations against your login until something works. The textbook version (one machine, sequential guesses) barely exists anymore — it's slow and easy to detect. The modern version is industrial.

What you actually face: a botnet of tens of thousands of compromised devices, each making one or two login attempts per minute against your site, distributed across IP ranges so no single source looks suspicious. The attacker isn't typing passwords — they're feeding leaked credential lists from past breaches into automated tools, hoping someone on your site reused a password from a service that got hacked. This is called credential stuffing, and it's the dominant variant in 2026.

There are two doors into your WordPress site for these attacks: `/wp-login.php` (the normal login form) and `/xmlrpc.php` (a legacy API endpoint that also accepts credentials and supports up to 1,000 password guesses in a single request — a single XML-RPC call can do what would take a thousand login-form requests).

If you're not sure whether attempts are succeeding, the diagnostic walkthrough covers the warning signs. If your access logs show the pattern but no admin accounts you don't recognise, you're being attacked but the attacks haven't broken in yet. That's the window where defense pays off.

Why WordPress is the single most attacked CMS

Three reasons, in order of significance:

First: scale. WordPress runs more than 40% of the web. Attackers automate against the platform that yields the most successful breaches per dollar of compute. The attacks aren't personal; they're statistical.

Second: standardised endpoints. Every WordPress site has `/wp-login.php` at a predictable URL. Every WordPress site (by default) has `/xmlrpc.php` enabled. An attacker doesn't have to discover the login URL — they assume it. By contrast, a custom-built admin at `/admin-7f2e1` is invisible to bulk attacks even if it has the same underlying authentication.

Third: user-enumeration leaks. The default WordPress configuration leaks valid usernames through several endpoints (the REST API `/wp-json/wp/v2/users`, the author archive at `/?author=1`, and others). Once an attacker has a list of valid usernames, the brute-force is half-solved — they only need to guess the password, not the username. The user-enumeration fix-guide covers exactly which endpoints leak and how to plug them.

None of these are bugs. They're defaults. Defaults that made sense when WordPress was a blogging tool in 2005 are now liabilities at the platform's current scale. The good news: each of them is fixable with a small amount of configuration, no plugin required.

Where the attacks land — the two doors

Door 1: `/wp-login.php`. The standard login form. Attacks here look like a high volume of POST requests to that URL, distributed across many IPs. Each request is a single username/password attempt. A modern attack drips one or two attempts per IP per hour to stay below per-IP rate limits, then rotates to a new IP. The aggregate rate against your site can be hundreds of attempts per minute.

How to spot it in logs: filter your access log for `POST /wp-login.php` and tally by hour. A normal site sees a handful of POSTs per day (your own logins, plus a few automated probes). A brute-forced site sees hundreds or thousands. Most hosting control panels include a way to view these logs; on the command line, `grep 'POST /wp-login.php' /var/log/nginx/access.log | wc -l` gives you a count, and `grep 'POST /wp-login.php' access.log | awk '{print $1}' | sort -u | wc -l` tells you how many distinct IPs were involved.

Door 2: `/xmlrpc.php`. The XML-RPC endpoint exists to let mobile apps, Jetpack, trackbacks, and other automation post to your site. It accepts credentials, and importantly, it accepts the `system.multicall` method that lets an attacker bundle up to 1,000 login attempts in a single HTTP request. This is the brute-force amplifier. A brute-force botnet hitting `/xmlrpc.php` can attempt millions of password combinations per hour with very little traffic noise.

If you don't actively use XML-RPC for an external integration (most sites don't), the right move is to block it entirely — the XML-RPC fix-guide covers how. If you do need it (Jetpack, the WordPress mobile app), restrict it to known-good IPs at the web-server or firewall level rather than leaving it world-accessible.

The defenses that actually work, in priority order

Four controls, ordered by how much each one reduces attack surface in practice. If you do only one, do the first.

1. Two-factor authentication. 2FA neutralises the entire credential-attack category — brute force, password spraying, credential stuffing, all of it. Even if an attacker guesses or knows your password, they can't log in without the second factor. The 2FA setup walkthrough covers the plugin choice and rollout plan. If you do nothing else this week, do this.

2. Block XML-RPC if you don't use it. This single change closes Door 2 entirely. Two lines in your `.htaccess` or a single nginx directive. No plugin needed. If you use Jetpack or the WordPress app, restrict by IP rather than block.

3. Strong unique passwords + a password manager. The breaches that fuel credential stuffing are full of reused passwords. A 24-character random password from a password manager makes credential stuffing useless against your site (the password isn't on any leaked list because you've never used it anywhere else). This is necessary but not sufficient — pair with 2FA.

4. Limit login attempts and add a delay. A plugin like Limit Login Attempts Reloaded blocks an IP after N failed attempts. Useful as a noise-reducer (your logs become readable), and it does meaningfully slow down the dumb single-IP attacks. Less effective against distributed botnets — but combined with the other three, it's a meaningful layer.

Defenses that look reassuring but don't move the needle much

Three popular controls that get oversold:

Moving `/wp-login.php` to a custom URL (security through obscurity). Plugins like WPS Hide Login move the login to e.g. `/secret-login`. This stops naive bulk attacks that hardcode `/wp-login.php`, but a serious attacker fingerprints the site (the login form has distinctive HTML) within minutes. Useful as a noise-reducer; useless as a defense in depth. Pair with real authentication controls — never rely on it.

CAPTCHA on the login form. Modern brute-force tools solve CAPTCHAs at scale via human-CAPTCHA-farm services (a few cents per thousand). reCAPTCHA v3 (frictionless) helps slightly more than v2 (image puzzles), but neither stops a determined attacker. Adds login friction for real users in exchange for limited gain. Skip it unless you're seeing real-time interactive attacks (rare).

IP allowlisting via plugin. The idea: only allow logins from your specific IPs. Useful in narrow cases (you're a one-person team with a static IP). Breaks the moment your IP changes (mobile, traveling, ISP rotation). For most sites the operational cost of being locked out yourself outweighs the security gain. Better implemented at the web-server / Cloudflare level than via WordPress plugin.

What the popular `Wordfence` and `iThemes/Solid Security` plugins add to the mix: a managed firewall layer in front of the login that blocks known-bad IPs based on a reputation list. Useful as a free pre-filter; not a substitute for any of the four real defenses above. The strongest combination: 2FA + block XML-RPC + password manager + Wordfence-style firewall = defense in depth without the theatre.

If a login eventually succeeded — what to do next

The signs that brute-force broke through: admin accounts you don't recognise, your own admin password no longer works, redirects on the front-end, malware on the file system, or a Google security warning.

Treat the site as compromised. The cleanup walkthrough covers the full sequence — backup-everything-first, change every password the site touches, file scan, database pass, close the entry point, verify, harden. Don't skip the close-the-entry-point step: if brute force worked once, the next attacker is already trying.

Specifically for brute-force-driven compromises, the close-the-entry-point step means all four defenses from §4, not just one. 2FA alone won't help on the day of the next attack if the attacker grabbed an API key or session cookie before you locked them out.

After the cleanup, the standard hardening checklist closes the rest of the common attack classes that aren't credential-related. Brute force is the most common; it's not the only one.

Set up monitoring once the site is clean. Weekly automated scans catch new vulnerabilities in your installed plugins as they're disclosed — that's the channel attackers will pivot to once they realise the credentials route is closed. GuardingWP Pro does this; so do the comparable Wordfence and Sucuri tiers.

Related fix guides

GuardingWP checks your site for the 11 most common WordPress vulnerabilities — plus scans your installed plugins against the known CVE database. Free, no account required.

Scan to see if your login surface is wide open →

Prefer to have this handled for you? Get this fixed — Full Hardening ($149)

Want continuous monitoring instead of one-off checks? See Pro plans from $9/month →