Search the Internet for «securing WordPress» and you will see no shortage of articles discussing various plugins that promise to make your website more secure.
Many of what you’ll find is valid — I even use a few such plugins. However, plugins can only do so much. Security-minded WordPress admins should also be wary of installing too many plugins, as doing so increases the surface area of attack. For these reasons, I prefer to use Nginx as an added security layer for my WordPress sites. Below are 5 helpful tips for hardening a WordPress site with Nginx.
1. Use HTTPS
I hope you’re already doing this, but if not, please secure your WordPress site with an SSL certificate. Google prioritizes HTTPS websites for a reason — they’re secure and enhance browsing privacy. SSL certificates are CHEAP — with 1-year certificates priced less than the cost of a McDonald’s value meal you cannot blame cost for your reason for not securing your website. Services like Let’s Encrypt are available too, but what you save in cost you can easily make up for in time. So, while I personally use Let’s Encrypt for some of my needs, I don’t exclusively use it, and nor do I recommend it to non-technical individuals. Instead, look at ssls.com (owned by the registrar Namecheap). There you can find Comodo certificates routinely for about $5 a year.
Now that you have a shiny new SSL certificate, how do you secure WordPress? With Nginx that’s easy.
The above Nginx configuration looks unwieldy, but luckily it’s pretty much something you can copy and paste. The gist of it is that despite the first S in SSL meaning «secure», not all deployments of SSL are secure. Numerous security vulnerabilities have been identified with older ciphers and protocols, and to properly secure a site you need to properly limit what dialect of SSL your server will speak.
To help visualize this cryptic formula I like to use the SSL Server Test by Qualys. This free test will inspect your server’s SSL configuration and analyze it against know exploits. The end result is an easy to read scorecard and breakdown analysis. As of the time of this writing, the above configuration gets an A+
2. Redirect HTTP to HTTPS
With step 1 complete, we can turn off HTTP entirely! There used to be an argument for only using HTTPS when necessary (i.e., login screens, banking, e-commerce checkout, etc). Developers jumped through complicated hoops to avoid serving insecure (e.g. HTTP) content on a secure page, lest a user’s browser warn them of insecure content. All of this was done in the name of avoiding, what at the time was, taxing cryptographic calculations. Luckily, it’s the year 2019, not 1999, and there exists virtually no reason to serve content over HTTP any longer.
The sample configuration above is about as simple as it gets. All HTTP traffic will be redirected to HTTPS while retaining any URL parameters. Should users type in your domain name directly into their browser’s address bar, they will be seamlessly and automated redirected. By using a 301 — which is the HTTP code for a permanent redirect — their browser will cache this lookup, and search engines will entirely ignore the HTTP version of your site. A win-win!
3. Rate Limit Logins
The number of automated hacking attempts a public facing site receives is amazing. Most of these are simply automated scripts looking for sites which foolishly left default user accounts and password enabled. You’re likely not at any risk, provided you’re following proper password security (e.g. using a password manager like 1Password, and not reusing passwords between sites, etc), but they are annoying. Not just annoying, but they can quickly fill up server logs, making it difficult to spot legitimate hacking attempts.
Luckily there is an easy way to fix this with Nginx — simply rate limit the login page. By rate limiting the login page you effectively set an upper bound to the number of login attempts, a single computer can perform during a certain timeframe. In short, it makes brute forced attempts impractical. Since most of these are automated mass attempts to hacking, they will quickly grow bored of the slow pace and move on to other websites.
This example involves two distinct components. The first is on line 1, where a limit_req_zone is defined. There are a few technical components to that line that I will not dive into here — for those curious, please check out the Nginx documentation for limit_req_zone. Suffice it to say that this setups a bit of memory (5 megabytes to be exact) and uses that to store information about the IP addresses that have recently hit the website. It also defines an acceptable rate of no more than 5 requests per minute.
The second part of this configuration is on line 10, with the limit_req configuration. No rate limiting is enabled until a limit_req directive is specified in a location block. In this example, we’re saying that the WordPress login page (/wp-login.php) should be rate limited using the shared memory zone called jason_whitehorn_us.
The most confusing part of this is the last bit on line 10 — the burst=5 nodelay configuration. The Nginx docs get into all the details, but the short of it is that this permits for someone to use all 5 of their requests immediately. Otherwise, a rate limit of 5 requests per minute is interpreted to mean «no request should arrive any sooner than one every 20 seconds». For some scenarios, this might make sense, for a login page it does not. Consider the use case where you mistakenly type your password and only realize it after hitting submit — would you wait 20 seconds, or immediately try again? With this additional configuration, we permit for the more reasonable scenario, provided that the total number of attempts never exceeds 5 in a 1 minute period.
4. Block XML-RPC
If you don’t know what XML-RPC is you’re not alone. It’s an obscure feature of WordPress that is being deprecated. Until it is fully removed from WordPress, however, it represents a source of abuse for would-be hackers. Unless you use the mobile WordPress admin app for iPhone or Android there is little reason to keep it around. Luckily Nginx can easily block all access to it with a simple configuration.
By simply returning a 404 right from Nginx we shut down all access to the XML-RPC feature. If you are hesitant about outright blocking it — perhaps you enjoy the mobile admin app — then you can easily rate limit this edit point using the previously mentioned technique.
5. Client Certificates For Login
This technique is not for everyone, but for those willing to setup client certificate auth in Nginx the benefits could be worth it. I wrote a piece earlier this month on all the gory details of setting up client certificate based auth with Nginx, so I will not repeat those here. Let’s look instead at how that technique applies to WordPress.
Above you can see a real-life example of how I am using this technique on this blog. The login URL protected by client certificate authentication. This works for this site because I am the sole author and nobody else has logins. For sites that allow account registration this technique is impractical.
The Big Picture
The above 5 techniques are just an example of how Nginx can secure websites, including WordPress. All of these examples were taken from the actual Nginx configuration that powers this website — which you can see in full on GitHub. Nginx can do so much more, so don’t hesitate to explore. I’m always finding new ways to tweak my Nginx configuration.