My Vultr firewall group rules

A Few Steps to Secure a WordPress Site

Use a Password Manager

The very first layer of security starts with your password.

You could have the most secure server and WordPress setup ever, but if your password is “qwerty” or some other equally awful password, then all that security is for nothing.

Good passwords need to be lengthy, randomized, and every site and service you use should have it’s own unique password. Do NOT re-use passwords. Ever.

I recommend using KeePass, KeePassXC or Bitwarden.

Then the only the password you need to remember is your master password. Which should be more of a phrase than just a single pass”word”. It should be something you can remember, long, and include upper/lower case, numbers, and special characters.

All other passwords should be generated by your password manager.

Cloudflare Firewall Rules

Cloudflare provides of a number of benefits that many people probably already use such as their Content Delivery Network (CDN), DDoS mitigation, Bot filtering, and caching. All of which is offered for free!

In addition to all those benefits, Cloudflare also offers a very easy to use firewall to help protect your application/website.

For any of my WordPress sites there are at least three firewall rules I deploy each time.

WordPress Admin Protection

The WordPress admin protection rule is used to challenge any visitor who tries to access the WordPress admin panel (/wp-admin), unless it’s my static IP address. This rule does a great job at blocking those random login attempts.

cloudflare wordpress admin protection


(http.request.uri.path contains "/wp-login.php" and not ip.src in {X.X.X.X})

Be sure to replace the X.X.X.X with your own static IP address. If you don’t have a static IP address you can remove the and rule.

Block bad bots

The next rule is a vast collection of bots I don’t wish to access my sites. The list of bots are compiled various sources – my own experience, 6G and 7G WordPress firewalls and other lists of “bad” bots.

Since the user agent field is case sensitive, I use the lower() function which converts the string to lowercase. This ensure the bots are blocked no matter if they are uppercase, lowercase or mix case.

This rule may or may not be no longer needed as Cloudflare added a bad bot blocking feature (Firewall > Bots > Bot Fight Mode) a little while back. However Cloudflare doesn’t list what bots they do or don’t block, so I still use this firewall rule to ensure I’m blocking the ones I certainly don’t want on my sites.

cloudflare wordpress block bots


(lower(http.user_agent) contains "acapbot") or (lower(http.user_agent) contains "acoonbot") or (lower(http.user_agent) contains "ahrefsbot") or (lower(http.user_agent) contains "attackbot") or (lower(http.user_agent) contains "backdorbot") or (lower(http.user_agent) contains "becomebot") or (lower(http.user_agent) contains "blackwidow") or (lower(http.user_agent) contains "blekkobot") or (lower(http.user_agent) contains "blexbot") or (lower(http.user_agent) contains "bunnys") or (lower(http.user_agent) contains "casper") or (lower(http.user_agent) contains "checkpriv") or (lower(http.user_agent) contains "cheesebot") or (lower(http.user_agent) contains "chinaclaw") or (lower(http.user_agent) contains "choppy") or (lower(http.user_agent) contains "cmsworld") or (lower(http.user_agent) contains "copyrightcheck") or (lower(http.user_agent) contains "datacha") or (lower(http.user_agent) contains "discobot") or (lower(http.user_agent) contains "dotbot") or (lower(http.user_agent) contains "dotnetdotcom") or (lower(http.user_agent) contains "dumbot") or (lower(http.user_agent) contains "emailcollector") or (lower(http.user_agent) contains "emailsiphon") or (lower(http.user_agent) contains "emailwolf") or (lower(http.user_agent) contains "extract") or (lower(http.user_agent) contains "flaming") or (lower(http.user_agent) contains "foobot") or (lower(http.user_agent) contains "g00g1e") or (lower(http.user_agent) contains "gigabot") or (lower(http.user_agent) contains "go-ahead-got") or (lower(http.user_agent) contains "gozilla") or (lower(http.user_agent) contains "grabnet") or (lower(http.user_agent) contains "harvest") or (lower(http.user_agent) contains "httrack") or (lower(http.user_agent) contains "jetbot") or (lower(http.user_agent) contains "kmccrew") or (lower(http.user_agent) contains "linkextractor") or (lower(http.user_agent) contains "linkscan") or (lower(http.user_agent) contains "linkwalker") or (lower(http.user_agent) contains "loader") or (lower(http.user_agent) contains "mechanize") or (lower(http.user_agent) contains "miner") or (lower(http.user_agent) contains "netmechanic") or (lower(http.user_agent) contains "netspider") or (lower(http.user_agent) contains "ninja") or (lower(http.user_agent) contains "octopus") or (lower(http.user_agent) contains "pagegrabber") or (lower(http.user_agent) contains "petalbot") or (lower(http.user_agent) contains "planetwork") or (lower(http.user_agent) contains "postrank") or (lower(http.user_agent) contains "pycurl") or (lower(http.user_agent) contains "queryn") or (lower(http.user_agent) contains "queryseeker") or (lower(http.user_agent) contains "scooter") or (lower(http.user_agent) contains "seekerspider") or (lower(http.user_agent) contains "semrushbot") or (lower(http.user_agent) contains "sindice") or (lower(http.user_agent) contains "sitebot") or (lower(http.user_agent) contains "siteexplorer") or (lower(http.user_agent) contains "sitesnagger") or (lower(http.user_agent) contains "smartdownload") or (lower(http.user_agent) contains "sogou") or (lower(http.user_agent) contains "sosospider") or (lower(http.user_agent) contains "spankbot") or (lower(http.user_agent) contains "spbot") or (lower(http.user_agent) contains "sqlmap") or (lower(http.user_agent) contains "stackrambler") or (lower(http.user_agent) contains "stripper") or (lower(http.user_agent) contains "sucker") or (lower(http.user_agent) contains "suzukacz") or (lower(http.user_agent) contains "suzuran") or (lower(http.user_agent) contains "teleport") or (lower(http.user_agent) contains "telesoft") or (lower(http.user_agent) contains "true_robots") or (lower(http.user_agent) contains "turingos") or (lower(http.user_agent) contains "vampire") or (lower(http.user_agent) contains "webwhacker") or (lower(http.user_agent) contains "whatcms") or (lower(http.user_agent) contains "woxbot") or (lower(http.user_agent) contains "wpscan") or (lower(http.user_agent) contains "xaldon") or (lower(http.user_agent) contains "yamanalab") or (lower(http.user_agent) contains "zmeu")

WordPress Content Protection

There are a lot of things going on in this firewall rule, but it does a great job of protecting against a number of WordPress exploits.

At a high level, the firewall rule is first going to block anyone who’s CloudFlare threat score is greater than 14.

Next it’s going to block access to any sensitive WordPress files (wp-config.php) or any direct .php access in the wp-content folder.

One of the most important parts of the rule is to block xmlrpc.php. This file is one of the most widely used files for WordPress exploits and unless you know exactly what this is and are using it, you should be blocking access to it.

The firewall rule is also going to block access to other system and environment files as well as possible backup files and has rules in place to help reduce or eliminate enumeration.

cloudflare firewall wordpress content protection


(cf.threat_score gt 14) or
(http.request.full_uri contains "wp-config.") or
(http.request.uri.path contains "/wp-content/" and http.request.uri.path contains ".php") or
(http.request.uri.path contains "phpmyadmin") or
(http.request.uri.path contains "/xmlrpc.php") or
(http.request.full_uri contains "passwd") or
(http.request.uri.query contains "author_name=") or
(http.request.uri.query contains "author=" and not http.request.uri.path contains "/wp-admin/export.php") or
(http.request.uri contains "/wp-json/wp/v2/users/") or
(http.request.full_uri contains "../") or
(http.request.full_uri contains "..%2F") or
(http.request.full_uri contains "vuln.") or
(http.request.uri.query contains "base64") or
(http.request.uri.query contains "<script") or
(http.request.uri.query contains "%3Cscript") or
(http.request.uri.query contains "$_GLOBALS[") or
(http.request.uri.query contains "$_REQUEST[") or
(http.request.uri.query contains "$_POST[") or
(http.request.uri contains "<?php") or 
(http.request.uri contains ".sql") or
(http.request.uri contains ".bak") or
(http.request.uri contains ".cfg") or
(http.request.uri contains ".env") or
(http.request.uri contains ".ini") or
(http.request.uri contains ".log") or
(http.request.full_uri contains "/license.txt") or
(http.request.full_uri contains "/readme.html")

Terraform Cloudflare Firewall for WordPress

If you want an automated and easily repeatable process then doing a lot of copying and pasting each time you deploy a new site. Well I have created a Terraform file that will very easily deploy the above rules.

You can get the Terraform file here: https://github.com/miketabor/terraform-cloudflare-wordpress-firewall

Terraform will prompt you for two items, first an API token and then domain name you want to apply the rules to.

cloudflare terraform apply

How to create a CloudFlare API Token

  1. Log into CloudFlare and go to: My Profile > API Tokens and click on Create Token.
    cloudflare api tokens create token
  2. Now give your new token a name and the following three permissions. READ for Zone:Zone and Zone:Zone Settings. Then give EDIT for Zone:Firewall Services.
    cloudflare token

Your web host

Linode, Upcloud and Vultr have all been some of my favorite VPS providers for a while now, however one host I’ve been using more and more lately is Vultr.

Vultr offers a nice range of VPS offerings, in various locations, and their High Frequency VPS is just flat out great in regards to performance. But that’s not all, Vultr offers a firewall (as many other providers do too) but the biggest difference is Vultr has a predefined “Cloudflare” firewall rule.

In just two rules, I can force all HTTP/HTTPS traffic to have to come through Cloudflare. Which is exactly what I want to ensure all traffic is being filtered through all the great Cloudflare offerings along with those Cloudflare firewall rules we added in the section above.

My Vultr firewall group rules

To do this I simply create a Vultr Firewall Group named “Standard-Firewall” which I apply to all my VPS machines. This firewall group allows SSH (port 22) from two static IPs I use and then forces all HTTP (port 80) and HTTPS (port 443) through Cloudflare. Any other connections are dropped.

Be sure to allow other ports if you use control panels such as RunCloud or similar, otherwise the Vultr firewall will block those connections.

Similar Posts

Leave a Reply

Your email address will not be published.