I have a client who was having chronic server downtime on GoDaddy. A customer support rep pointed me to the Resource Usage page on cPanel which showed the CPU was maxed out. When I investigated the Apache logs, I discovered that several of the sites on the server were under DDoS (distributed denial of service) attack targeting the xmlrpc.php and wp-login.php files. This is how I mitigated those attacks without using Cloudflare and without getting a bigger server.
How You Know You’re Under Attack
A DDoS attack can massively slow down the load time of your site or make the site completely inaccessible, with the browser spinner going forever until it eventually times out.
If you go into your web hosting control panel, there’s usually a place where you can see CPU usage. On GoDaddy Business Web Hosting, there’s a page on cPanel called “Resource usage”. If you click that, you’ll see your site’s CPU usage. Spikes in usage are a hint that you are under attack, especially if there is no other reason your site should be getting high traffic at the moment.
To really prove that you’re under attack, though, you’ll need to check out your server Apache access logs. In GoDaddy, this in cPanel in the Metrics section. Click “Raw Access” to get to your logs and then click on the website in your server with the largest log file (you can sort by clicking the Disk Usage header).
In the log file above, you can see something is probing wp-login.php and xmlrpc.php almost every second in various subdirectories of the site. If you see something like this, you know you’re under attack.
The knee-jerk solution to combating DDoS attacks is to use Cloudflare. That works, but it involves pointing your DNS at Cloudflare, and then pointing Cloudflare at your server. In my case, the DNS was controlled by the clients, so it would be a pain to get them to change it.
Another solution is to simply get more computing power on your server. But, this costs money, and in my mind is a form of defeat, since the attack isn’t really blocked.
XML-RPC is a service that is used for inter-site communications with your site that is mostly unused these days except during Jetpack setup. If you’re unsure of what it does, you should do some research to make sure that disabling it is OK with your site.
There are tons of articles online on how to disable WordPress’ XML-RPC file, but many of these methods do NOT mitigate DDoS attacks on that file!
There are several plugins that can disable XML-RPC, or you can add some code yourself in your functions.php to do it. While these do prevent access to your site via XML-RPC, they do not prevent WordPress resources (i.e., CPU) to be used when xmlrpc.php is visited. Thus, these do NOT mitigate DDoS attacks to xmlrpc.php!
There is also a popular snippet of code floating around that blocks XML-RPC from your .htacess file:
<Files xmlrpc.php> order deny,allow deny from all </Files>
When I tried this, and visited xmlrpc.php in my browser, I got redirected to my site’s “Not Found” page. While that is fine for blocking access, it still requires your server to go through all of the motions of loading WordPress and rendering up your “Not Found” page. I wanted to avoid all of that!
My Solution for Apache
I wanted to find a way to truly block attacks on xmlrpc.php and wp-login.php without using PHP or WordPress resources, thus sparing precious CPU bandwidth.
Blocking xmlrpc.php is one thing, but wp-login.php is actually needed to log into WordPress, so that can’t just be blocked!
In my case, the attacker was targeting wp-login.php in subdirectories such as “/old”, “/wp”, “/test”, “/blog”, and so on (you can see them in the log above). So, I could try to target wp-login.php in just those directories!
After lots of research and experimentation, I came up with this .htaccess code to do the blocking for xmlrpc.php and wp-login.php in specific directories:
# DDOS ATTACK MITIGATION BY BRIAN 2020-06-20 RedirectMatch 403 ^/old/wp-login.php RedirectMatch 403 ^/cms/wp-login.php RedirectMatch 403 ^/wp/wp-login.php RedirectMatch 403 ^/test/wp-login.php RedirectMatch 403 ^/blog/wp-login.php RedirectMatch 403 ^/backup/wp-login.php RedirectMatch 403 ^/wp1/wp-login.php RedirectMatch 403 ^/2019/wp-login.php RedirectMatch 403 ^/2020/wp-login.php RedirectMatch 403 ^/2021/wp-login.php RedirectMatch 403 ^/2022/wp-login.php RedirectMatch 403 ^/2023/wp-login.php RedirectMatch 403 xmlrpc.php ErrorDocument 403 forbidden
If you place this after the other code in your .htaccess file, it will return a 403 forbidden error for any attempt to access xmlrpc.php altogether, and wp-login.php in those particular directories only.
The last line is important to prevent displaying your site’s “Not Found” page in WordPress. With this code, WordPress is never loaded and the browser just displays “forbidden”.
You can confirm it’s working by trying to access xmlrpc.php or wp-login.php (in the specified directories) in your browser.
Note, this code is tailored to stop wp-login.php attacks in subdirectories. If you have attacks on this file in the root, it might be best to block all IP addresses to that file except your own, i.e., whitelisting.
Finally, this code works on Apache servers. You’ll need different code for NGINX.
After I implemented the .htaccess code above, my CPU usage went down to normal:
And, checking my log files, I saw that the attacks were now returning 403 instead of 200!
Another Example: Autodiscover Attack
I had another case where Outlook and someone’s mobile phone were trying to hit the /autodiscover/autodiscover.xml file on the server:
I blocked the whole directory with this:
# STOP AUTODISCOVER "ATTACK" 7/17/2020 BY BRIAN RedirectMatch 403 ^/autodiscover/.*
Blocking IP Addresses
Sometimes you’ll see attacks coming from a single or a small set of IP addresses. It’s easy to just block these IP addresses entirely.
First, check with your hosting provider. They usually have a place to do it. In GoDaddy, go to cPanel -> Security -> IP Blocker and simply enter the IP addresses.
If you want to block IP’s from your .htaccess file, you can do it with this line:
Deny from 18.104.22.168
Let me know if these worked for you. Do you have other solutions to block DDoS attacks on WordPress? Let me know in the comments below! – Brian
Please Leave a Question or Comment
Oh man, I’ve been working on mitigating these attacks for a while, and even the standard bit wasn’t helping prevent all of the attacks. Thanks for this! I’ll give the redirect matches a try!
Hope it works for you!
Did you get here through my site, https://www.swingdance.la/, or is it just a coincidence?
I haven’t made it out to Old Towne Swing but if I’m ever down in OC I’ll check it out!
Yeah, I was looking around swingdance.la and ran across the link to this site. I’ll be reading it for sure. Thank you for your useful resources, Brian! I’ve worked with WordPress, including plugin and theme development, on and off for years, and the XML-RPC brute force attacks have been *really* infuriating as of late. If you don’t already know-I recently learned that WordPress allows URL access to some user information – such as usernicename. Usernicename is created at the time of username creation, but it’s a sanitized version of the username, so it will drop everything to lowercase as it gets put into the database. Problem with that is, if your original username is all lowercase, they’ve got everything they need for the brute force attack to work. It doesn’t help with CPU access that they can keep trying of course, but it lets you know that you need to change usernicename in the database to be different from your username. Your solution is even better, but the additional hardening doesn’t hurt.
It’d be great to have you out for Old Towne Swing. We’re small and rebuilding, as is everyone else, I suppose. It’s got a really nice energy and friendly crowd, though! I run OTS very similarly to how I ran Strutters’. It was just time to do it on my own.
Wow, very cool that you’ve done WP development! We definitely need to talk next time I make it to OC.
What hosting do you use? I’ve seen a lot of attacks on GoDaddy. I host my sites on Gridpane and haven’t had a problem. They have wpfail2ban implemented on their servers. It blocks xml-rpc, username enumeration, and other bad stuff at the server level. There’s also a WordPress plugin, https://wordpress.org/plugins/wp-fail2ban/, but I haven’t tried that.