Harden WordPress after Initial Setup for Security and Safety

By | February 22, 2017

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.

wordpress-guide

This article is for documenting the things that I do after 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.

Update only Core

define( 'WP_AUTO_UPDATE_CORE', true );

Update only Plugins

add_filter( 'auto_update_plugin', '__return_true' );

Update only Themes

add_filter( 'auto_update_theme', '__return_true' );

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’s 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 for your website components

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 your website’s static components

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 Expires Header to static components 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 catching.

# 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

Disable HTML in the comments section

There is no need for people to put HTML in their comments unless your blog certainly caters to that audience type. For most people, disabling HTML will be the best option. Add the following code to the functions.php of your WordPress theme.

add_filter( 'pre_comment_content', 'esc_html' );

Adjust Auto-save intervals

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.

define( 'AUTOSAVE_INTERVAL', 90 ); // 90 seconds

Make sure to add this line before the following line:

require_once(ABSPATH . 'wp-settings.php');

You can also try disabling them all together if that’s your thing. To do that, insert the following line to the wp-config.php instead of the above.

define( 'AUTOSAVE_INTERVAL', false );

Stop WordPress from Guessing URLs

Add the following line of code to your theme’s functions.php.

remove_filter('template_redirect', 'redirect_canonical');

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 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');

Leave a Reply

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