Last week we looked at the logs of a small business WordPress site we host. The owner's complaint was simple: the site felt slow, but hardly anyone was visiting. Both things turned out to be true.
In one day the site got about 9,400 requests. Only about a third came from real people. The rest were bots. Around 1,350 of those requests were password guesses on the login page, and one bot alone sent hundreds of them in a few minutes. A few times that day, a single source fired 20 to 40 requests in a second or two.
Nothing was hacked. The site was just very busy serving visitors who weren't people. We've seen this pattern on a lot of WordPress sites, so here's what's going on and what you can do about it.
Why a few bots can slow down a whole site
Every time a WordPress page loads, the server runs PHP to build it. On shared hosting, each site gets a limited number of PHP workers that can do this at the same time.
Think of them as cashiers at a small store. Two or three are plenty when customers come in a few at a time. But when a bot sends 30 requests in one second, every cashier is busy, and your real visitor waits in line. If the wait gets too long, the site slows down or shows a brief error that goes away when you refresh.
That's also why your analytics look quiet. Google Analytics and similar tools ignore most bots, so the traffic that's actually hurting you never shows up there.
If you want to check, your security plugin's log (Wordfence, Limit Login Attempts Reloaded and so on) will show failed logins. In cPanel, Metrics > Raw Access has the actual request log. Or just open a ticket and ask us. We can tell you roughly how much of your traffic is real.
Before you change anything
A few of the fixes below edit .htaccess or wp-config.php in your site's folder (usually public_html). Download a copy of both first. In cPanel File Manager, click Settings and tick Show Hidden Files, otherwise you won't see .htaccess.
If something breaks, upload your copy back and the site is exactly as it was. Our guide on WordPress Backups and Testing Changes Safely has more on this.
1. Protect the login and block xmlrpc.php
This one gives the biggest win, honestly. Password guessers go after two files: wp-login.php, the normal login page, and xmlrpc.php, an older way for apps to talk to WordPress. Bots like xmlrpc.php because they can try many passwords in one request.
Start with the basics. Use a strong, unique password for every admin account, and don't use "admin" as a username. Then install a login protection plugin like Limit Login Attempts Reloaded and turn on two-factor login for admins.
If nothing on your site uses XML-RPC, you can block it completely. Add this near the top of .htaccess, above the # BEGIN WordPress line:
# BEGIN Block xmlrpc.php
<Files xmlrpc.php>
Require all denied
</Files>
# END Block xmlrpc.php
Now the server turns those requests away before PHP even starts. To test it, open https://yourdomain.com/xmlrpc.php in your browser. You should get a "Forbidden" page. Then check that your home page loads and you can still log in.
Caution: Skip this block if you use Jetpack or the WordPress mobile app. Both need XML-RPC. Rely on the login plugin instead.
2. Let your cache skip PHP
Most WordPress sites already have a caching plugin. The catch is how the cached page gets delivered. On the site we looked at, the plugin was set to a mode where every cached page still went through PHP, so each bot hit still used up a worker.
If you use WP Super Cache, here's the switch:
- Go to Settings > WP Super Cache > Advanced and make sure caching is on.
- Under Cache Delivery Method, choose Expert, then click Update Status.
- Scroll down to Mod Rewrite Rules and click Update Mod_Rewrite Rules.
- Open a few pages in a private browser window, then check the Contents tab to see that pages are being cached.
If anything looks off, set it back to Simple. Other caching plugins have a similar setting, often called "disk enhanced" or "mod_rewrite" mode.
Keep in mind that caching only helps logged-out visitors. Login attempts and form posts are never cached, which is why step 1 still matters.
Some owners also put Cloudflare in front of the site. It can filter a lot of bad bots before they reach us.
Caution: When you move your DNS to Cloudflare, copy every record over, and keep your mail records (MX and mail.yourdomain.com) set to "DNS only", not proxied. Otherwise email can stop working. We're happy to check your records with you first.
3. Turn away lazy scanners
Real browsers always say what they are (that's the "user agent"). A lot of scanners don't bother. You can refuse requests with no user agent by adding this to .htaccess, also above # BEGIN WordPress:
# BEGIN Block empty user agents
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} ^-?$
RewriteRule ^ - [F]
</IfModule>
# END Block empty user agents
After saving, test your home page, your login and your contact form.
Bots also read your list of usernames from the REST API so they know which accounts to attack. Don't switch the REST API off, since the block editor and many plugins need it. Just hide the user list from logged-out visitors. Save this as wp-content/mu-plugins/hide-rest-users.php (create the mu-plugins folder if it isn't there):
<?php
// Hide the REST API user list from visitors who are not logged in.
add_filter( 'rest_endpoints', function ( $endpoints ) {
if ( ! is_user_logged_in() ) {
unset( $endpoints['/wp/v2/users'] );
unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
}
return $endpoints;
} );
And keep WordPress and your plugins updated. Scanners are mostly looking for old, known bugs. See WordPress Updates, Plugins and Themes.
4. Move WP-Cron to a real cron job
WordPress has a built-in scheduler, WP-Cron, that handles scheduled posts, plugin checks and emails. By default it runs when someone loads a page. So when bots pile in, background tasks can start at the worst moment.
First, open wp-config.php and add this line above /* That's all, stop editing! */:
define('DISABLE_WP_CRON', true);
That only stops page visits from triggering it. Your tasks still run, because next you'll set up a proper schedule. In cPanel, open Cron Jobs, set Minute to */15 and the other fields to * (every 15 minutes), and use this as the command:
wget -q -O - "https://yourdomain.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1
If you'd rather use curl, this does the same thing:
curl -s "https://yourdomain.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1
The >/dev/null 2>&1 bit stops cPanel from emailing you every 15 minutes. (You'll thank us for that.) Scheduled posts may now go out up to 15 minutes late. If you run timed promos, use */5 instead. Ask us if you prefer the PHP command.
A few smaller things
Crawlers. SEO tools and AI crawlers can fetch dozens of pages in a few seconds. A robots.txt file in your site's main folder can ask SEO bots to slow down:
User-agent: AhrefsBot
Crawl-delay: 10
User-agent: SemrushBot
Crawl-delay: 10
If you'd rather AI companies not train on your content, you can also add User-agent: GPTBot, User-agent: CCBot or User-agent: ClaudeBot, each followed by Disallow: /. That's your call. It doesn't affect Google search. Just never block Googlebot or Bingbot. And if your SEO plugin manages robots.txt, edit it there instead.
The PHP error log. A warning that repeats on every page load is wasted work, and the log can quietly grow huge. On that same site, the theme was trying to load a file that no longer existed, and it added thousands of warnings a day. The log is often a file called error_log in public_html, or ask us and we'll tell you where yours is. Look for one message repeating, fix it (or send it to whoever maintains the theme), then empty the old log. On a live site, keep WP_DEBUG set to false.
If you host with us
Open a support ticket and we'll look at your traffic and help block abusive sources at the server level. Tell us your domain and roughly when the site felt slow.
Quick questions
Will blocking bots hurt my Google ranking?
No. None of the steps above block Googlebot or Bingbot. Blocking AI training bots doesn't change your normal search results either.
I edited .htaccess and now the site shows an error. Help?
Don't panic. Upload the copy you saved, or remove the lines you just added. A "500 Internal Server Error" right after an edit is usually just a typo. Then try again, one change at a time.
Do I need two security plugins to be safe?
No, one is enough. In our experience, two firewall plugins on the same site often slow it down or fight each other.
