WordPress

How to Stop WordPress Login Brute Force Attacks with Cloudflare

Updated October 2026 6 views 9 min read
WordPress | Updated October 2026

Brute force bots hammer wp-login.php on every WordPress site, guessing passwords thousands of times a day from constantly changing IP addresses. If your site runs behind Cloudflare, you can stop almost all of that traffic at Cloudflare's edge with a single WAF rule, before it ever reaches your server. This guide shows you how to build that rule correctly, including the one detail most guides get wrong that quietly lets the attacks through.

What This Protects, and What It Does Not

Cloudflare can only filter traffic that actually passes through it. This rule protects requests that reach your site through Cloudflare's network. It does not help with:

  • Requests sent straight to your server's IP address, bypassing Cloudflare
  • Attacks from inside your own WordPress (a compromised plugin or theme)
  • Password guessing against services other than the website, such as FTP or email

That is why the last two sections cover hardening WordPress itself. The Cloudflare rule stops the flood at the edge; the WordPress side catches anything that gets through.

01. Why wp-login.php Gets Attacked

WordPress powers a huge share of the web, and every WordPress site has its login form at the same place: /wp-login.php. That predictability makes it a standing target. Automated bots crawl the internet looking for WordPress sites and then submit login attempt after login attempt, trying common usernames like "admin" against lists of leaked passwords.

You will usually notice it one of three ways:

  • A flood of failed-login notification emails if you run a security plugin that reports them.
  • The site feeling slow, because every login attempt loads WordPress, runs PHP, and queries the database, which burns through your account's resources.
  • Resource-limit warnings or a temporary account suspension when the attack is heavy enough. See Error 508/503 Resource Limit Reached.

The attacks almost always come from many different IP addresses, often through Tor or hijacked machines, so blocking one address at a time never ends. The effective answer is to challenge the request pattern itself, which is exactly what a Cloudflare WAF rule does.

Why Not Just a Plugin

A login-limiting plugin helps, but it only acts after the request has already reached your server and started WordPress. The whole point of filtering at Cloudflare is to stop the request before it costs you any PHP or database work. Use both: Cloudflare to absorb the volume, a plugin as a backstop.

02. Before You Start: Proxy Your Site Through Cloudflare

Cloudflare only sees your traffic when your DNS records are proxied through it. In the Cloudflare dashboard, open DNS and check that the records for your site (usually the root domain and www) show an orange cloud, not a grey one. A grey cloud means Cloudflare is only answering DNS and your traffic goes straight to the server, where no WAF rule can touch it.

If you have not set Cloudflare up yet, start with our Cloudflare Setup Guide. The rule below works on every Cloudflare plan, including the free one.

Tip

Keep your real server IP address out of public view. If a DNS record anywhere (an old mail or ftp subdomain, for example) points directly at the server with a grey cloud, attackers can find that IP and skip Cloudflare entirely. Proxy what you can, and avoid publishing the raw IP.

03. Build the Login Protection Rule

In the Cloudflare dashboard, select your domain, then go to Security > WAF > Custom rules (in the newer dashboard layout this is under Security > Security rules). Click Create rule and give it a name like WordPress Login Protection.

Cloudflare lets you build the condition with dropdown menus or paste it as an expression. The expression is exact and easy to copy, so switch the rule to the Edit expression view and paste this in:

(http.request.uri.path contains "/wp-login.php" and http.request.method eq "POST")
or (http.request.uri.path contains "/wp-admin"
    and not http.request.uri.path contains "/wp-admin/css/"
    and not http.request.uri.path contains "/wp-admin/js/"
    and not http.request.uri.path contains "/wp-admin/images/"
    and http.request.uri.path ne "/wp-admin/admin-ajax.php")

Then set the action:

  • Choose action: Managed Challenge
  • Place at: leave the default order

Click Deploy. That is the whole rule. The next two sections explain the two parts that make it both effective and safe, because this is where most guides go wrong.

Hosting More Than One Site on This Cloudflare Account

A Cloudflare zone covers one domain, so a rule in that zone only affects that domain and you do not normally need to name the host. If you point several domains at one zone using subdomains, scope the rule to the right site by adding http.host eq "yourdomain.com" and (...) around the condition above.

04. Match the Whole Login Path, Not Just the Exact File

This is the detail that matters most, and the one that quietly defeats a lot of login-protection rules.

Most guides tell you to match the path where it equals /wp-login.php. That looks correct, but it only matches that exact text. Attackers know this, so they send the request to /wp-login.php/ instead, with a trailing slash. Web servers still hand that off to the same login script and process the password attempt, but a rule that checks for an exact match sees /wp-login.php/ as a different path and lets it straight through. The attack continues as if you had no rule at all.

The fix is to match with contains rather than equals:

Weak  (misses the trailing-slash trick):
    http.request.uri.path eq "/wp-login.php"

Strong (catches /wp-login.php and /wp-login.php/ and anything after):
    http.request.uri.path contains "/wp-login.php"

The rule in the previous section already uses contains, so if you pasted it you are covered. If you built your rule by hand with the dropdowns, open it and confirm the operator on the login path reads contains, not equals.

Important

The same idea applies to any path-based rule you write. A single trailing character, an extra slash, a mixed-case spelling, or a query string can all be used to dodge a too-exact match. When you are blocking a specific script, matching on contains the script name is far harder to evade than an exact match.

05. Keep the Login Page and Dashboard Working

Two choices in the rule keep it from getting in the way of real use.

Only challenge the POST, not the page view

Loading the login page is a GET request. Submitting the login form is a POST. The rule challenges only the POST, so your login page still loads normally for everyone, and the challenge appears only at the moment credentials are actually submitted. That keeps the experience clean for real people while still stopping the bots, which have to POST to guess a password.

Let the dashboard load its own files

The /wp-admin part of the rule protects the admin area, but the dashboard also loads its stylesheets, scripts, and images from under /wp-admin/. If you challenge those, the login and admin pages render unstyled and broken. The rule excludes /wp-admin/css/, /wp-admin/js/, and /wp-admin/images/ so those load freely. It also excludes admin-ajax.php, which many themes and plugins call from the public front end for things like contact forms, so leaving it challenged would break features for ordinary visitors.

Managed Challenge vs Block

Managed Challenge is the right default. It is nearly invisible to real browsers, automatically harder on suspicious ones, and it does not permanently lock anyone out. Use the stronger Block action only if you are certain no legitimate person needs that path, because a block with a mistake in it can lock you out of your own admin area.

On Paid Plans: Rate Limiting

Cloudflare's paid plans add Rate Limiting rules, which can challenge or block an IP only after it makes, say, more than five POSTs to /wp-login.php in a minute. That is an excellent complement to the rule above, because it reacts to behavior rather than just the path. The custom rule in this guide works on every plan and is the right starting point.

06. Shut Down XML-RPC Too

WordPress has a second door that attackers love: /xmlrpc.php. It is an older remote-access interface that lets a single request try many passwords at once (through a feature called system.multicall) and can also be abused to bounce junk traffic off your site. Most modern sites do not need it.

If you are sure nothing you use depends on it, add a second Cloudflare custom rule to block it outright:

Field:     URI Path
Operator:  contains
Value:     /xmlrpc.php
Action:    Block
Check Before You Block

A few tools still use XML-RPC, most notably the Jetpack plugin and the official WordPress mobile app. If you rely on one of those, use Managed Challenge instead of Block, or skip this rule and disable the specific XML-RPC features you do not need from inside WordPress. Our guide on disabling comments, pingbacks, and trackbacks covers the pingback piece that XML-RPC is most often abused for.

07. Harden WordPress Itself

The Cloudflare rule absorbs the volume. These steps make sure anything that does reach WordPress still fails. None of them take long and together they close the gap:

  1. Do not use "admin" as a username. If your administrator account is named admin, create a new administrator with a different name, log in as that account, and delete the old one, assigning its content to the new account.
  2. Use a long, unique password for every WordPress account, especially administrators. A password manager makes this painless. A strong password alone defeats the entire point of password guessing.
  3. Add two-factor authentication with a reputable plugin. Even a correctly guessed password is useless without the second factor.
  4. Install a login-limiting plugin as a server-side backstop, so repeated failures from a single source are throttled even if they reach WordPress.
  5. Keep WordPress, themes, and plugins updated, and remove anything you no longer use. Most hacked sites are compromised through outdated code, not guessed passwords.

For the full checklist, see WordPress Security: The Complete Hardening Guide. If you suspect a site has already been compromised, work through How to Fix a Hacked WordPress Site.

08. Test the Rule Without Locking Yourself Out

After you deploy the rule, confirm it behaves the way you expect:

  • Load your login page at yourdomain.com/wp-login.php in a normal browser. It should open normally, because viewing the page is a GET and only the POST is challenged.
  • Log in as usual. With Managed Challenge, a real browser passes through automatically or after a brief check. If you are asked to confirm you are human, that is expected and only takes a moment.
  • Watch it work. In the Cloudflare dashboard under Security > Events, you will see challenged login attempts listed against your new rule. During an active attack this fills up quickly, which confirms the rule is catching them.
Avoid Locking Yourself Out

If you choose the Block action anywhere instead of Managed Challenge, create a higher-priority Skip rule for your own IP address first, so you are never blocked from your own admin area. If a server-level firewall ever blocks you after too many failed attempts, follow Firewall Block: How to Unblock Your IP to get back in.

Want Us to Set This Up for You?

If you would rather we review your Cloudflare configuration or build these rules for you, open a ticket and we will take care of it.

Open a Support Ticket

Quick Recap: Stopping WordPress Login Attacks

If you only do these things, do these:

  1. Proxy your site through Cloudflare so the orange cloud is on and your traffic actually passes through the WAF
  2. Create a Managed Challenge custom rule on the POST to your login path and on the admin area
  3. Match the login path with contains, not equals, so the trailing-slash trick (/wp-login.php/) cannot slip past it
  4. Exclude the admin css, js, images, and admin-ajax.php so the dashboard and front-end features keep working
  5. Harden WordPress too with a non-admin username, a strong password, and two-factor login as a backstop
Share: X in Reddit f Email
  • 0 Users Found This Useful

Was this answer helpful?

Related Articles

Create Static Files with your WordPress Install

WordPress | Updated January 2026 WordPress generates pages dynamically on every request,...

Elementor Critical Error with Both Elementor and Elementor Pro

WordPress | Updated March 2026 When both Elementor (free) and Elementor Pro are active and...

How to Disable WordPress Comments Pingbacks and Trackbacks

WordPress | Updated March 2026 WordPress enables comments, pingbacks, and trackbacks by...

How to Install WordPress

WordPress | Updated March 2026 Installing WordPress on your Ultra Web Hosting account takes...

How to Back Up WordPress with UpdraftPlus

WordPress | Updated July 2026 Every Ultra Web Hosting account is backed up at the server...



Save 30% on web hosting - Use coupon code Hosting30