Protect and speed up your WordPress website

With the ever increasing madness on the internet it is more important than ever to protect your website. While I’m not security expert every now and then I search for useful things to use to protect my websites. These tips are a collection of what I found and learned on the internet that apply to my situation. I think they are useful for most people using WordPress – Especially if you’re tired of plugins like WordFence, Sucuri and similar “security” plugins that only make you paranoid and waste your precious server resources.

And remember, your server already has a good firewall and network protections in place – Your hosting provider does that to protect its servers.

However, since they do not cover your website specifically I’ve applied a bunch of the following methods to help protect my website from spam, login attempts and brute force attacks. All of these tips also work for ClassicPress.

A few years ago I already wrote a post about improving speed and safety. This is a sort of continuation to that post. I did replace several methods and added new ones since then.

Note: In this article we’ll edit the .htaccess file which is essential to your WordPress site and by extension your server. Doing this wrong will cause your site to malfunction in one way or another. So be sure you make a backup of the .htaccess file before doing anything and know how to undo such changes so you can quickly revert to a working copy if you do something wrong.
What I usually do is duplicate the file and rename it to a.htaccess as a way of having a way to restore mistakes. Don’t forget to remove the duplicate when you’re done.

If you’re not comfortable editing such files or you’re not sure how any of this works ask someone to help you!

1. Use browser caching

This is a performance tip. By default caching will work fine for most things. But specifying how long and what the browser should cache should speed up loading times for your website considerably. Especially for returning visitors. Images don’t really change so those can be cached for much longer than other files. As defined below.

I’ve placed this near the top of my .htaccess file.

# Cache Control
ExpiresActive On
# Images
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# Video
ExpiresByType video/webm "access plus 1 year"
ExpiresByType video/mp4 "access plus 1 year"
ExpiresByType video/mpeg "access plus 1 year"
# Fonts
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/otf "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
# CSS, JavaScript
ExpiresByType text/css "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
# Others
ExpiresByType application/pdf "access plus 1 year"
ExpiresByType image/ "access plus 1 year"

2. Use Compression

The server can be instructed to use compression for sending data to visitors. Speeding up load times. The compression method requires the module mod_deflate to be active on your hosting, most servers have this.
Check in your hosting dashboard like DirectAdmin or cPanel if ‘GZIP Compression’ is enabled for your website. Ask your hosting provider where the option is if you can’t find it.

# Gzip compression
SetOutputFilter DEFLATE
# Don’t compress images and other uncompressible content
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|rar|zip|exe|flv|mov|wma|mp3|avi|swf|mp?g|mp4|webm|webp)$ no-gzip dont-vary
# Compress all output labeled with one of the following MIME-types
AddOutputFilterByType DEFLATE application/atom+xml application/javascript application/json application/rss+xml application/ application/x-font-ttf application/xhtml+xml application/xml font/ttf font/otf font/opentype image/svg+xml image/x-icon text/css text/html text/plain text/x-component text/xml
Header append Vary: Accept-Encoding
# Force deflate for mangled headers
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding

3. Hide your folder contents

This one line will prevent anyone from loading folders and seeing a list of files. This helps obscure where certain files are and what the servers file structure is.

I’ve placed this directly below the caching directives.

# Prevent directory listing
Options -Indexes

4. Force the use of SSL/HTTPS

This requires a valid SSL certificate for your website. If you do not have this, skip this tip.
Adding the code is a bit finicky as it ideally goes between the WordPress mod_rewrite lines. Mod_rewrite is a module for your server that makes the permalinks.

The easy way to add this is by placing the following below the hiding folder contents (or near the top) in your .htaccess file.

RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

But as you’ll see we also initialize the rewrite engine here, which WordPress already does further down in the .htaccess file.
So what I did instead is replace the WordPress rewrite code with this:

RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
# WordPress
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

Use either method, both will work just fine. If you choose to replace the WordPress code you may find that WordPress adds a new set of rules at the bottom of the .htaccess file. You can simply delete that. Some servers are a bit insistent with it. So experiment a bit which way works best for you.

5. Block basic hacks and exploits

With exploits and hacks emerging regularly it’s also smart to block access to a bunch of these things. Even if a script is vulnerable or a malicious file ends up in your setup bots and external requests can’t really use it.

Similar as with the SSL redirect I’ve added this ‘between’ the WordPress rewrite rules. I’ve placed these directly below the SSL redirect.

# Hack/backdoor Security
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
# Block user Enumeration exploits
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{QUERY_STRING} ^/?author=([0-9]*)
RewriteRule .* - [R=403,L]
# Block unused request methods
RewriteRule .* - [F,L]
# Prevent Script Injection
Options +FollowSymLinks
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|[|%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|[|%[0-9A-Z]{0,2})
RewriteRule ^(.*)$ index.php [F,L]

This prevents loading of certain files from external sources – Similar to image deeplinking and it’ll prevent (or slow down) the figuring out user id through the author pages. And attempts to block certain injection attacks.

I also block requests to XMLRPC. This is a communication feature of WordPress used by some external apps. More modern and advanced apps and extensions use a proper API these days. So if you use, for example, the official WordPress or Jetpack app that’ll keep working.

# Block WordPress xmlrpc.php requests
<Files xmlrpc.php>
order deny,allow
deny from all

I’ve placed this below the caching code.

6. Basic brute force protections

This should prevent scripts from brute forcing their way onto your website through forms and login pages. This should also help against comment spam. If you look on line 4 you’ll see that you need to enter your domain name here. Replace YOURDOMAIN.COM with your website address without ‘www.’ in front of it.

Make sure it’s correct or the comments and logins may not work reliable anymore.

# Bruteforce Protection
RewriteCond %{REQUEST_URI} .*/(wp-comments-post|wp-login)\.php.*
RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule (.*) - [R=403,L]

What this does is everything that wants to load a comment form or login form needs to come through your website.
All direct access gets redirected to a ‘Unauthorized’ page.

7. A few plugins to strengthen security

While I will never recommend a firewall plugin I do use a few security plugins and antispam plugins.

To get rid of spam I used to use Akismet from Automattic. This connects your site to their server which then filters spam out.
Recently Automattic started implementing paid tiers for Akismet, and I’ve switched to Antispam Bee. A plugin that does exactly the same, except it doesn’t use an external server. View the plugin page here »

Top stop bots and fake accounts from registering I also use my own plugin, No-Bot Registration. This introduces a simple security question to forms preventing automated submissions. View No-Bot Registration here »
Set up a smart and relatable question for your users. You can personalize this a bit to make it relevant for your website as well.

And to combat login brute forcing I use Limit Login Attempts Reloaded. This is a lightweight plugin that blocks IP addresses temporarily when too many login attempts are made. View the plugin page here »
You may want to whitelist your own IP Address if you’re prone to make login mistakes a lot.

8. Non-standard ports

Ask your hosting provider to change some server ports.
FTP by default goes through port 21. Which is fine, but every hacker knows this as well. Change it to some other port and make it a tiny bit more difficult for them. This is relevant because FTP sends passwords unencrypted, and thus leaves your login out in the open. If you do not use FTP at all you can also close the port and disable the FTP service all-together like I did.

A safer alternative is sFTP, where the ‘s’ stands for Secure. sFTP goes through port 22. And changing the port is a smart idea too.
On top of that, of-course use a good diverse and long-ish password.
If you’re on a mac, or use some kind of password manager other than macOS Keychain, use its password generator to make a strong password.

In closing

If you try and use these tips and tricks protecting your website is not very hard. It does not require bulky security plugins and definitely doesn’t need to be as daunting as it seems to be. I review these methods once every year or so, or when I think about it and add or remove methods with whatever makes sense at the time.

Do you use different snippets and tricks to protect your website? Maybe we can share and learn from each other.

Leave a Reply

Your email address will not be published. Required fields are marked *

Leave the field below empty!