2026年11月12日
2026年11月12日
Limit WordPress Login Attempts with Cloudflare Rate Limiting and Custom Rules
This article is a localized edition of the Japanese original. Read the Japanese original
Your WordPress login page (/wp-login.php) receives password brute-force attempts every day. If your site is served through Cloudflare, you can stop this traffic at Cloudflare before it ever reaches WordPress. This article walks through two methods step by step: using Cloudflare rate limiting rules to stop repeated requests from the same IP, and using custom rules to challenge or block access from anyone other than administrators.
Screen names follow the Cloudflare dashboard (English UI) and official documentation as of October 2026. If your dashboard is set to another language, look for the corresponding items.
Prerequisite: traffic must go through the Cloudflare proxy
Cloudflare rules only apply to requests that pass through the Cloudflare proxy.
- In the dashboard's DNS settings, make sure the proxy status of your site's A / AAAA / CNAME records is "Proxied" (orange cloud). Rules do not apply to "DNS only" records.
- Requests sent directly to your server's IP address bypass Cloudflare, so rules won't stop them. Also set up the "Prevent direct access to the origin" section later in this article.
For setting up Cloudflare itself, see How to set up Cloudflare for WordPress.
Differences between plans
Rate limiting rules and custom rules are available on the Free plan, but the number of rules and the conditions you can use vary by plan. The main values in the official documentation as of October 2026 are:
| Item | Free | Pro | Business |
|---|---|---|---|
| Number of rate limiting rules | 1 | 2 | 5 |
| Fields usable in rate limiting conditions | Path, Verified Bot | Host, URI, Path, Query, and more | Everything to the left plus method, source IP, User Agent, and more |
| Counting period | 10 seconds | Up to 1 minute | Up to 10 minutes |
| How long the block lasts | 10 seconds | Up to 1 hour | Up to 1 day |
| Number of custom rules | 5 | 20 | 100 |
On the Free plan, rate limiting can only count in short units of "up to N requests per 10 seconds," and blocks lift after 10 seconds. That slows down fast brute-force attempts but does nothing against slow ones. On the Free plan, the practical approach is to combine it with the custom rules described below.
Method 1: Stop repeated requests with a rate limiting rule
This setting blocks an IP address for a set time when it sends many requests to /wp-login.php in a short period.
- In the Cloudflare dashboard, open your domain.
- Open "Security" → "Security rules" (in the older dashboard, the "Rate limiting rules" tab under "Security" → "WAF").
- Select "Create rule" → "Rate limiting rules".
- In "Rule name," enter a recognizable name such as
wp-login rate limit. - Under "When incoming requests match," set "Field" to "URI Path," "Operator" to "equals," and "Value" to
/wp-login.php. - Leave "With the same characteristics" set to "IP" (Free and Pro plans support IP only).
- Under "When rate exceeds," set the number of requests and the period. On the Free plan the period is 10 seconds; set the count to something like 5.
- Under "Then take action," choose "Block" in "Choose action," and set "Duration" (how long the block lasts).
- Select "Deploy" to save and activate.
The condition from step 5 looks like this as an expression. Selecting "Edit expression" below the condition fields lets you type the expression directly ("Use expression builder" takes you back to the fields).
(http.request.uri.path eq "/wp-login.php")
A guideline for choosing the count: a normal login sends a few requests to /wp-login.php, such as displaying the login screen (GET), submitting it (POST), and the redirect after login. Pick a count that won't trigger if you mistype your password a few times, and after deploying, log in yourself to make sure there's no problem.
On Business and higher plans you can use the request method in conditions, so the following expression counts only POST requests (not displays of the login screen).
(http.request.uri.path eq "/wp-login.php" and http.request.method eq "POST")
Method 2: Challenge non-admins with a custom rule
If administrators only log in from known locations (such as an office with a fixed IP), apply a Managed Challenge to, or block, requests to /wp-login.php from all other IPs. A Managed Challenge has Cloudflare check the browser and ask for interaction only when needed. People can get through to the login screen, but most scripted automatic submissions stop here.
- Open "Security" → "Security rules" and select "Create rule" → "Custom rules".
- Enter a "Rule name" (for example
wp-login challenge). - Open "Edit expression" and enter the following expression.
203.0.113.10and198.51.100.0/24are examples; replace them with your administrators' IP addresses (separate multiple entries with spaces). - In "Choose action," select "Managed Challenge" and deploy with "Deploy".
(http.request.uri.path eq "/wp-login.php" and not ip.src in {203.0.113.10 198.51.100.0/24})
If administrators don't have fixed IPs, you can drop the and not ip.src in {…} part and apply the Managed Challenge to all requests. It puts little burden on people and reduces automated brute-force attempts.
The official documentation recommends starting with Managed Challenge instead of Block, reviewing matched requests in Security → Analytics, and then switching to Block. Following the same order is the safe way to avoid locking administrators out.
Avoid blocking all of /wp-admin/ under the same condition. /wp-admin/admin-ajax.php is also called from the public side of the site (forms and plugin features), so blocking it can break visitor-facing features.
Protect xmlrpc.php too
/xmlrpc.php is an older mechanism for controlling WordPress from external apps, and password brute-forcing happens there as well. If you don't use it, block it with a custom rule.
(http.request.uri.path eq "/xmlrpc.php")
However, if you use services that rely on XML-RPC, such as Jetpack or the official WordPress mobile app, blocking it will break them. If you're not sure whether you use it, start with a Managed Challenge, check Security → Analytics for legitimate use, and then decide.
Prevent direct access to the origin
If an attacker knows your server's IP address, they can target /wp-login.php directly without going through Cloudflare. Configure the server firewall to allow ports 80 and 443 only from Cloudflare's IP ranges (published on the official site at https://www.cloudflare.com/ips/) to close this loophole. The ranges can change, so review them periodically.
Notes when combining with WordPress-side plugins
Behind Cloudflare, the source IP of requests reaching your server is Cloudflare's. If you use a plugin that limits login attempts as is, different visitors can appear to share the same IP, and legitimate users may be locked out together.
The real visitor IP is in the CF-Connecting-IP header that Cloudflare adds. Configure Apache's mod_remoteip or Nginx's ngx_http_realip_module to trust Cloudflare's IP ranges, or use the plugin's setting for running behind a proxy, so that counting is based on real visitor IPs. For WordPress-side limiting, see How to limit login attempts in WordPress.
Checks after deploying
- While logged out, confirm that you can actually log in from an administrator's environment.
- Open
/wp-login.phpfrom a different connection (such as a smartphone's mobile network) and confirm the challenge or block appears as intended. - A few days later, look at Security → Analytics to see how many and which requests matched the rules, and adjust the counts and actions.
Summary
- Cloudflare rules only apply to traffic through the proxy (orange cloud).
- Use a rate limiting rule to stop repeated requests to
/wp-login.phpper IP. On the Free plan it counts in 10-second units, so its effect is limited. - Use a custom rule to apply a Managed Challenge to
/wp-login.phpfrom non-admin IPs, then switch to Block after watching the results. - If you don't use it, block
/xmlrpc.phptoo. Don't block all of/wp-admin/. - Accept origin traffic only from Cloudflare's IP ranges, and pass real visitor IPs to WordPress-side plugins.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)