Harden WordPress After Initial Setup for Security and Safety

Over the years, I have set up hundreds of WordPress installations. But every time I do that, I have to look around for guides and tips for making WordPress usable and secure. That takes up a lot of my time and repeating this step is always a pain in the butt.

This article is for documenting the things that I do after the initial WordPress setup. I’m writing this to keep a tab on the things I do. In future when I set up a WordPress website; I’ll use this guide to quickly do the initial setup which would otherwise take hours to do by searching and messing with stuff.

Add A Basic robots.txt

The robots.txt file should be present in the root directory of your WordPress installation. This file lets the search engines know what to and what not to index. Depending on what kind of content you have, you may want to modify this file. But, not having a robots.txt isn’t something that you should do. Here’s a basic version that I use for most sites that I set.

Sitemap: *your_sitemap_url.xml*

# global
User-agent: *
Allow: /wp-admin/admin-ajax.php
Disallow: /xmlrpc.php
Disallow: /wp-admin/

You can learn some best practices from Google’s Robots.txt Specifications.

Canonical robots.txt

By convention, the robots.txt should be in the root directory of your website. Most bots follow this convention. This isn’t however, a universal rule followed everyone. Some badly coded bots and scripts can scan your whole website to locate the robots.txt file. You can avoid this issue by telling every bot about your robots.txt location. Add the following code to the .htaccess file.

# START CANONICAL robots.txt
<IfModule mod_rewrite.c>
RewriteBase /
RewriteCond %{REQUEST_URI} !^/robots.txt$ [NC]
RewriteCond %{REQUEST_URI} robots\.txt [NC]
RewriteRule .* http://your_site_url/robots.txt [R=301,L]
# END CANONICAL robots.txt
</IfModule>

Canonical Sitemap.xml

Similar to how search engines are, you can also set up a canonical URL for the Sitemap.xml for your website. Add this code to your .htaccess file.

# START CANONICAL Sitemap.txt
<IfModule mod_alias.c>
RedirectMatch 301 /sitemap\.xml$ http://your_website_url.com/sitemap.xml
RedirectMatch 301 /sitemap\.xml\.gz$ http://your_website_url/sitemap.xml.gz
</IfModule>
# END CANONICAL Sitemap.txt

Enable Automatic updates for WordPress Core and Plugins

There is no point in worrying about security if you are running an outdated version of WordPress or plugins on your website. You can always try updating them manually, but that’s not always possible. You can automate this process by editing the wp-config.php and adding the following code to it.

define( 'WP_AUTO_UPDATE_CORE', true ); // update core only
add_filter( 'auto_update_plugin', '__return_true' ); // update plugins only
add_filter( 'auto_update_theme', '__return_true' ); // update themes only

You can add all three of these snippets to enable automatic updates for everything. Be warned, though. If some update breaks your site, you’ll have a hard time knowing which one did the deed.

Disable Server Signature

Whenever your server encounters an error, it reveals the full server signature to the user. You don’t want that to be visible to anyone for security. To disable that, add the following code to your .htaccess.

# BEGIN DISABLE SERVER SIGNATURE
ServerSignature Off
# END DISABLE SERVER SIGNATURE

Stop People from Browsing your server directories

Some servers allow anyone to view the files present in them. This can cause a lot of issues with security because they can see what stuff you have there. To stop that, add the following line to your .htaccess file.

# BEGIN PREVENT DIRECTORY BROWSING
Options All -Indexes
# END PREVENT DIRECTORY BROWSING

Disable external access to wp-config.php

No one except you should access the wp-config.php. This file stores your authentication keys. It also stores the salts for these keys, so if someone can access them, this can prove to be a massive security hole. Disable access to your wp-config.php file by adding the following directives to your .htaccess file.

# BEGIN PROTECT wp-config.php
<files wp-config.php>
order allow,deny
deny from all
</files>
# END PROTECT wp-config.php

Block access to readme.html

A lot of WordPress blogs don’t block access to their readme.html file. Though not a big issue, this can cause your WordPress version to be publicly available. You don’t want that.

# BEGIN BLOCK README.HTML
<files readme.html>
order allow,deny
deny from all
</files>
# END BLOCK README.HTML

Disable File Editing In The wp-admin Panel

If someone gets access to your wp-admin panel, they can mess around with your stuff. Most certainly with the code in your .htaccess and robots.txt files. Some plugins also allow you to edit some files without logging into your cPanel. You don’t want that. Add the following code snippet to the wp-config.php file to disable file editing.

define( 'DISALLOW_FILE_EDIT', true );

Disable Logging of PHP Errors

You shouldn’t publicly display PHP errors on your website. Disable them by adding the following directive to your .htaccess file.

# BEGIN BLOCK PHP ERROR REPORTS
php_flag display_errors Off
# END BLOCK PHP ERROR REPORTS

Enable GZIP Compression

The Internet is becoming fatter. That’s not okay. There’s no excuse when you can use GZIP on your site to compress web pages before they reach your readers. Small page size means decreased load times and better user experience. Add this to your .htaccess to enable GZIP compression.

<IfModule mod_deflate.c>
# Compress HTML, CSS, JavaScript, Text, XML and fonts
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml

# Remove browser bugs (only needed for really old browsers)
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
Header append Vary User-Agent
</IfModule>
# END ENABLE GZIP COMPRESSION

Add Expires Header To All The Static Resources

The static content on your website like CSS, JavaScript and images don’t change often. You can use this to your advantage in gaining speed. Adding the Expires Header to static resources speeds up things because your browser will cache them. This will result in fewer HTTP requests thus decreasing website loading time. This is essentially leveraging browser caching.

# BEGIN ADD EXPIRES HEADER
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 30 days"
ExpiresByType image/jpeg "access plus 30 days"
ExpiresByType image/gif "access plus 30 days"
ExpiresByType image/png "access plus 30 days"
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/pdf "access plus 1 month"
ExpiresByType text/x-javascript "access plus 1 week"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
ExpiresDefault "access plus 2 days"
</IfModule>
# END ADD EXPIRES HEADER

Don’t copy and paste this exact snippet to your .htaccess though. Adjust the values according to the kind of content you are serving. Use Expires Headers carefully.

Remove Query Strings

If you use a one-click WordPress installer, you’ll see that there is a .htaccess file already present in the directory of your installation. It will have some Directives already defined. To remove the query strings from your URLs, add the following code between the RewriteBase / and RewriteRule directives.

# BEGIN REMOVE QUERY STRING
RewriteCond %{QUERY_STRING} "post_type=" [NC]
RewriteRule (.*) /$1? [R=301,L]
# END REMOVE QUERY STRING

Redirect to HTTPS

If you want to use HTTPS on your website (you probably should), add the following lines in your .htaccess. Put them between the RewriteBase / and RewriteRule directives.

# BEGIN REDIRECT TO HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# END REDIRECT TO HTTPS

Adjust Post Draft Auto-save Interval

By default, WordPress auto-saves your posts every minute to avoid loss of your data. If you think that’s too frequent or too slow, then you can adjust this value. Add the following code to your wp-config.php and adjust the number to your liking.

require_once(ABSPATH . 'wp-settings.php');
// .
// . some code here
// .
define( 'AUTOSAVE_INTERVAL', 90 ); // 90 seconds

define( 'AUTOSAVE_INTERVAL', false ); // or add this to disable autosave

Disable Login Hints

You don’t want your hacker buddies to get access to your blog by brute-forcing their way in. When you enter a wrong username or password, or a combination of the two, WordPress tells you whether your username was wrong or the password. You should disable that. Add this code to the functions.php file.

function disable_login_hints() {
return 'Wrong credentials. :(';
}

add_filter('login_errors', 'disable_login_hints' );

Update WordPress Secret Keys

Your WordPress installation security keys are to prevent unauthorized access to your blog. You can update these keys to be more secure. Go to this page and copy the keys. Now, open the wp-config.php file from your WordPress installation and replace them one by one.

After doing that, all your session cookies will become expired. So, anyone logged into your website will be logged out.

Remove WordPress Version

It’s unnecessary and potentially a security risk to show your WordPress installation version to the whole world. You can disable the display of the WordPress version from everywhere by adding the following code to the functions.php of your theme.

function remove_wp_version() {
return '';
}

add_filter('the_generator', 'remove_wp_version');

I guess this should be enough.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.