A brute force attack is an automated attempt to guess your login credentials by trying thousands of username and password combinations. There is no exploit involved, no vulnerability to patch, no clever technique. The attacker points a script at your login page and tries passwords until one works or until something stops them.
WordPress is the most targeted CMS on the internet, and wp-login.php is the most targeted endpoint on WordPress. Every WordPress site has it at the same URL. Every WordPress site has at least one admin account. Automated bots scan the internet continuously, find WordPress installations, and start trying credentials. Your site does not need to be important or well-known to be targeted. It just needs to exist.
How brute force attacks work#
Simple brute force
The attacker tries every possible password combination systematically. Starting with short passwords and working up, or cycling through common passwords first. Against a 6-character lowercase password, a fast script can exhaust the entire keyspace in minutes.
In practice, pure brute force against web login forms is slow because each attempt requires an HTTP request and a server response. But attackers are patient, and they distribute the work across thousands of IPs.
Credential stuffing
The more effective variant. Attackers buy databases of email/password combinations leaked from breaches of other services – LinkedIn, Adobe, Dropbox, and countless smaller sites. They test these credentials against WordPress login pages. If someone reused their LinkedIn password for their WordPress admin account, the attacker gets in on the first try.
Credential stuffing is not technically brute force in the traditional sense, but the mechanics are the same: automated scripts sending login attempts at scale. The difference is that credential stuffing uses real passwords that real people actually chose, making it far more efficient than random guessing.
Dictionary attacks
A middle ground between pure brute force and credential stuffing. The attacker uses a list of common passwords – “password123”, “admin2024”, “letmein”, the site’s domain name, the company name followed by numbers. These lists are curated from previous breaches and publicly available password studies. They are surprisingly effective because people are predictable in how they choose passwords.
Why WordPress is targeted#
Known login URL
Every WordPress installation has its login page at
/wp-login.php
or
/wp-admin/
. An attacker does not need to find the login page – it is at a predictable, standardized path. Scanning the internet for WordPress sites and then targeting their login pages is trivial to automate.
Known usernames
WordPress makes username discovery easy. The default admin account is often “admin”. Author archives are at
/author/username/
, leaking valid usernames. The REST API at
/wp-json/wp/v2/users
returns a list of users with their usernames on many sites. Some themes display the author’s login name on posts.
An attacker who knows a valid username only needs to guess the password. That cuts the problem in half.
xmlrpc.php
WordPress’s XML-RPC endpoint supports a method called
system.multicall
that allows multiple function calls in a single HTTP request. An attacker can send a single POST request containing hundreds of
wp.getUsersBlogs
calls, each with a different password. One HTTP request, hundreds of login attempts.
This is far more efficient than hitting wp-login.php repeatedly. Rate limiting based on request count does not help because the attempts are packed into a single request. The server processes all of them before any login protection can respond.
The scale of the problem
This is not hypothetical. Every WordPress site with a public login page receives brute force attempts. The question is not whether your site is being attacked but how much traffic it is absorbing. For a detailed look at what this looks like from the server side – the access logs, the resource consumption, the patterns – see what happens when bots find your WordPress login page.
On a typical day, a WordPress site with no protection receives dozens to hundreds of brute force login attempts. Popular sites or sites that have appeared on bot target lists receive thousands. Each attempt consumes server resources: a PHP worker processes the request, a database query checks the credentials, and a response is generated. Multiply that across sustained attacks and it adds up.
Application-level protection#
These are defenses you configure inside WordPress or through plugins. They work but they have a fundamental limitation: every request still reaches your server and consumes resources before the protection kicks in.
Strong, unique passwords
The most effective single defense. A long, random password generated by a password manager is immune to dictionary attacks and impractical to brute force. If every admin account on your site uses a unique 20+ character random password, brute force attacks will fail.
The problem is enforcement. You can use a strong password, but can you guarantee every other admin on the site does too? WordPress does not enforce password complexity by default. A single weak admin password negates everyone else’s good practice.
Two-factor authentication
2FA adds a second verification step after the password. Even if the attacker guesses the correct password, they cannot log in without the second factor (typically a time-based code from an authenticator app).
This is the single most effective defense against credential stuffing. A password stolen from a breach is useless without the second factor. Wordfence, iThemes Security, and dedicated 2FA plugins all offer this. Enable it on every admin account.
Limit login attempts
Plugins like Limit Login Attempts Reloaded or the login protection in Wordfence block an IP address after a configurable number of failed login attempts. After 5 failures, the IP is locked out for 30 minutes (or whatever duration you configure).
This stops a single IP from cycling through a password list. But sophisticated attackers rotate through thousands of IPs, staying under the threshold on each one. Each IP tries 4 passwords, moves on, and comes back later. The individual lockout threshold is never triggered, but the aggregate attack continues.
CAPTCHA on the login page
Adding a CAPTCHA (Google reCAPTCHA, hCaptcha, or similar) to the login form requires solving a challenge before the login attempt is processed. This stops basic automated scripts that cannot solve CAPTCHAs.
Modern CAPTCHA-solving services are cheap and fast. Attackers can outsource CAPTCHA solving at scale for a fraction of a cent per solve. CAPTCHAs raise the cost of an attack but do not eliminate it. They also add friction for legitimate users, which is a tradeoff worth considering.
Disable xmlrpc.php
If you do not use XML-RPC (Jetpack and the WordPress mobile app use it, most sites do not), disable it entirely. Add this to your
.htaccess
:
<Files xmlrpc.php>
Order Deny,Allow
Deny from all
</Files>
Or in Nginx:
location = /xmlrpc.php {
deny all;
}
This eliminates the multicall amplification attack entirely. If you use Jetpack, you can whitelist Jetpack’s IPs instead of blocking xmlrpc.php completely.
Change the login URL
Security-through-obscurity plugins like WPS Hide Login change wp-login.php to a custom URL. This stops automated bots that blindly target the default path. It does not stop a determined attacker who can discover the real login URL through other means (by checking for login redirects, for example).
It reduces noise in your logs and cuts down on resource consumption from drive-by bots. It is not a security measure in any meaningful sense, but it reduces the volume of junk traffic.
Why application-level protection is not enough#
Every application-level defense shares the same fundamental problem: the attack request has already reached your server before the defense can act.
When a brute force attempt hits wp-login.php, even with Wordfence installed:
- The web server (Nginx or Apache) receives the request
- The web server passes it to PHP-FPM
- PHP starts a worker process
- WordPress core loads
- The database connection is established
- WordPress loads active plugins, including Wordfence
- Wordfence evaluates the request
- Wordfence decides to block it
Steps 1 through 6 consume server resources regardless of the outcome. A PHP worker is occupied. A database connection is used. Memory is allocated. CPU cycles are spent. Only at step 7 does the protection kick in.
Under a sustained brute force attack – thousands of requests per minute from rotating IPs – your server is spending significant resources processing requests that will be blocked. PHP workers are tied up handling attack traffic instead of serving legitimate visitors. On a server with 10 PHP workers, an attack consuming 8 of them leaves only 2 for real visitors. Your site slows down or returns 503 errors while WordPress is technically doing its job of blocking the attacks.
This is not a criticism of Wordfence or any other security plugin. It is a structural limitation of any defense that runs inside the application it is protecting. The work happens before the defense loads.
Server-level protection#
Server-level defenses operate before the request reaches PHP. They block at the web server or network level, consuming minimal resources per blocked request.
Rate limiting at the web server
Nginx’s
limit_req
module can throttle requests to wp-login.php:
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
# ... rest of PHP handling
}
This limits each IP to 1 login request per second with a burst of 3. Excess requests are rejected by Nginx before PHP starts. The rejected request costs almost nothing in server resources.
Rate limiting is effective against single-IP attacks but does not help against distributed attacks from thousands of rotating IPs, each sending requests slowly enough to stay under the limit.
fail2ban
fail2ban monitors your web server access logs for failed login patterns and bans offending IPs at the firewall level. Once banned, the IP’s packets are dropped at the network layer – the request never reaches the web server at all.
fail2ban is reactive. The failed attempts have to happen first, generate log entries, and then fail2ban responds. The first several attempts from each IP always get through. But once banned, the IP is blocked at the lowest possible level.
For self-managed servers, fail2ban with a WordPress login jail is one of the most effective defenses against brute force. It does not prevent the initial attempts, but it stops the sustained attack quickly and efficiently.
Web application firewall
A WAF like ModSecurity can detect and block brute force patterns at the web server level. The OWASP Core Rule Set includes rules for detecting rapid authentication attempts and blocking suspicious request patterns before PHP processes them.
Behavioral bot detection
The most effective server-level approach goes beyond rate limiting and signature matching. Instead of counting failed logins from a single IP, behavioral detection looks at multiple signals:
- Request patterns. An IP that only visits wp-login.php and xmlrpc.php with no page views is almost certainly a bot. Legitimate users browse pages before logging in.
- TLS fingerprinting. Real browsers produce recognizable TLS handshakes. Automated scripts using Python’s requests library or curl have distinctly different TLS fingerprints.
- Cross-site reputation. An IP that is brute forcing login pages on ten different sites is probably brute forcing yours too, even if it has not triggered any threshold on your individual site yet.
- Cookie behavior. Real browsers accept and return cookies. Many bot scripts ignore them.
- Geographic and network patterns. Login attempts from residential IPs in the same country as the site owner look different from attempts originating from cloud hosting providers in countries with no business relationship to the site.
These signals are scored together. An IP that triggers one signal might be innocent. An IP that triggers five is almost certainly malicious. This probabilistic approach catches distributed attacks that evade simple rate limiting because each individual IP sends only a few requests.
Protecting SSH too#
Brute force is not just a WordPress problem. If your server has SSH access, bots are probing it with credential stuffing just like they probe wp-login.php. The defense is simpler: disable password authentication entirely and use SSH keys. No password means no password to guess. The brute force attack becomes impossible, not just difficult.
How Hostney stops brute force attacks#
On Hostney, brute force protection is handled at the infrastructure level, before requests reach your WordPress installation. This is the most detailed tie-in in this series because the bot detection system was specifically built for this problem.
What happens when a brute force attack hits your site on Hostney
- The request arrives at the edge layer. Before the request reaches Nginx, before PHP starts, before WordPress loads, the bot detection system evaluates it.
- The IP is scored. Every IP that sends requests to sites on Hostney has a threat score computed from multiple independent signals: rate limit violations, scanner patterns, multi-target spread (hitting multiple unrelated sites), repeat offender history, TLS fingerprint anomalies, and more.
- Low-scoring IPs pass through. A legitimate visitor logging into their WordPress admin gets a low score and proceeds normally. No delay, no challenge, no friction.
- High-scoring IPs are blocked or challenged. An IP with a high threat score – because it is hitting login pages across multiple sites, sending requests with bot-like TLS fingerprints, or matching known attacker patterns – is blocked at the edge. The request never reaches PHP. No WordPress resources are consumed.
- Scores propagate across servers. When an IP is flagged on one server, the score propagates to all servers in the fleet within seconds. An attacker who starts brute forcing site A on server 1 is already flagged when they move to site B on server 2.
- Scores decay over time. If an IP stops attacking, its score gradually decreases. Legitimate users who triggered a false positive are not permanently blocked. This automatic decay reduces the operational burden of managing blocklists.
Why this matters for brute force specifically
The fundamental problem with brute force defense at the WordPress level is resource consumption. Even when every attempt is blocked, the server does the work of loading PHP, initializing WordPress, and connecting to the database for each request.
On Hostney, blocked brute force traffic consumes almost no resources. The decision happens at the edge layer in microseconds. A sustained attack of 10,000 login attempts per hour costs the same in server resources as a handful of legitimate page views – because the attack traffic is dropped before it reaches the application stack.
This means your site’s performance is unaffected during a brute force attack. PHP workers are available for real visitors. Database connections are not consumed by login attempts. The attack is absorbed by the infrastructure, not by your WordPress installation.
ModSecurity as a second layer
Even after the bot detection system, ModSecurity with the OWASP Core Rule Set runs on every request that reaches the web server. It catches attack patterns that the behavioral system might not flag – unusual request bodies, protocol violations, and known exploit signatures.
For brute force specifically, ModSecurity’s rules detect xmlrpc.php multicall abuse and block it at the web server level, preventing the amplification attack entirely.
What you should still do
Server-level protection does not replace basic security hygiene. On Hostney, as on any hosting platform:
- Use strong, unique passwords for every admin account
- Enable two-factor authentication on all admin accounts
- Remove unused admin accounts – every account is a potential target
- Keep WordPress and plugins updated – brute force is sometimes used to test credentials obtained through other vulnerabilities
- Consider disabling xmlrpc.php if you do not need it
The infrastructure handles the heavy lifting of blocking attack traffic. Your job is making sure that if any attempt does get through, the credentials it is trying do not work.
The layered approach#
No single defense stops all brute force attacks. The effective approach layers multiple defenses so that each one catches what the others miss:
| Layer | What it stops | What it misses |
|---|---|---|
| Strong passwords | All brute force and dictionary attacks | Nothing if truly random, but relies on every user complying |
| Two-factor authentication | Credential stuffing, even with correct password | Nothing if enabled; the gap is users who do not enable it |
| Login attempt limiting | Single-IP high-volume attacks | Distributed attacks from rotating IPs |
| CAPTCHA | Basic automated scripts | CAPTCHA-solving services, sophisticated bots |
| fail2ban | Sustained attacks after initial detection | First few attempts from each IP |
| Web server rate limiting | High-volume attacks from individual IPs | Slow, distributed attacks |
| Behavioral bot detection | Distributed attacks, credential stuffing, sophisticated bots | Novel attack patterns not yet in the scoring model |
The first two rows are your responsibility. The rest depend on your hosting environment and server configuration. On a self-managed VPS, you configure all of these yourself. On a managed platform with built-in bot detection, the infrastructure handles the lower rows automatically.
For WooCommerce stores, the stakes are higher because compromised admin access means potential access to customer data, order information, and payment flows. Every layer matters more when there is customer data behind the login page.