WordPress Security Hardening with .htaccess Rules

This way of protecting your website uses a combination of htaccess rewrite rules to harden WordPress sites at the server level, plugins to harden WP sites at the site level i.e internally, and plugins to detect changes to files and the database SQL injections.

There’s no need to be squeamish. Toughening WordPress is easier than it looks but there are caveats:

  • No site or server can ever be 100% secure from malicious bots, hacker scripts or other non desirables.
  • Denial of Service (DoS) attacks require a very different method of site protection.

The site defence methods shown here have been checked on hundreds of servers and have been well researched. They work.

Keep in mind that if your site is hacked even after you have used the methods outlined here then none of JournalXtra, me, the plugin developers or any of the originators of any of the scripts and htaccess directives outlined here can be held liable.

A bonus to using the WP security measures shown here is that your site’s 404 errors will be reduced considerably.

WordPress Security Toughening

Securing WordPress is best done during the site’s installation. There are a couple of changes to the way WordPress functions that are easier to implement immediately after installation. These changes make it difficult to launch automated attacks.

This WordPress security system is split into 7 parts:

  1. Bug fixing
  2. Security plugins
  3. Server protection & 404 reduction
  4. More WordPress 404 reduction
  5. Defences against query string, remote file inclusion, XSS & SQL injection and fingerprinting attacks.
  6. Protecting wp-login.php, wp-register.php and wp-signup.php.
  7. Disabling PHP easter eggs, proxy POST requests, bogus graphic exploits and more remote file inclusion (RFI) protection.

The protection offered by part 5 is strongest when implemented almost immediately after WordPress installation.

Part One: Bug Fixing

Update your plugins, update your themes, visit the update page and click update/reinstall WordPress. Updates often fix vulnerabilities. Securing your server and site will not protect WordPress much if your scripts are insecure.

Updating/Reinstalling WordPress, themes and plugins will overwrite any modified files. This will remove any malicious content placed within them as well as remove any customizations you’ve made to them.

Before updating, backup any customizations made to files so you can reintroduce them afterwards.

Part Two: The Plugins

This is the quick section. Download and install the following plugins. Do not activate them just yet.

The 4 plugins shown here are enough to protect a site and keep 404 pages to a minimum. If you want to read more about these plugins or learn about more WP security plugins then visit this JournalXtra post.

Part Three: Toughen the Server

If you use an Apache (Linux) server that has htaccess files enabled, you can change the server’s global configurations on a per directory basis using directives in htaccess files.

The short explanation: Apache reads every htaccess file in every directory it encounters on its way to finding a requested file such as htaccess in the root directory when index.php in example.com/index.php is requested or both the root htaccess and subdirectory htaccess when example.com/sudirectory/index.php is requested.

Apache obeys all directives in all htaccess files it encounters, even those that negate each other. Precedence is given to the directives in the most recent htaccess read as Apache serves the requested file.

Put the following htaccess directives in you server’s top-most htaccess file. For the majority of servers, this will be in the directory above public_html. If your server doesn’t call the top most directory public_html, just put the following directives in an htaccess file at the highest level directory your server allows.

If an htaccess file already exists, just add these directives to the bottom of the file. If the htaccess file doesn’t exist, create it. htaccess is a dot-file and the full stop in front of the file name is important so if you create one, name it .htaccess.

Always keep an empty new line at the end of your htaccess files. Some scripts write to htaccess files without starting a new line. This causes server errors because new data is appended to the last line of existing data.

Read the comments in the above blacklist. The URL to a bigger blacklist is mentioned in them.

When installing software that uses a script called install.php, such as WordPress, you must disable the final htaccess directive until the installation is complete. Temporarily deactivate it by changing “Deny from all” to “Allow from all”.

An alternative way to disable the protection of install.php is to place the following directive into the root htaccess of the site being installed:

Remember to remove it once the installation is complete.

Part Four: Reduce WordPress 404 Errors

404 errors are caused by missing files:

  • Files, posts, pages and directories that once existed but which you deleted
  • Files, posts, pages and directories that never existed on your server but which senile search engine bots, plugins and malicious scripts try to find anyway
  • Documents and directories that do exist but which have been backlinked to with bad characters in the backlinks so that your server can’t find them.

WordPress serves virtual files, mostly. Uploads are non virtual but the actual posts and pages that people see only exist in the WP database unless served from a file cache by caching plugins.

When your server receives a request for a directory or document, the server reads the htaccess files along the document path until it reaches the document.

When the server encounters the WordPress htaccess rewrite rules it is instructed to check whether the requested directory is a real directory and whether the requested document is a real document. If it is not a document that exists on the server, the server (Apache) is instructed to load the WordPress index.php script. This loads WordPress and WordPress then fulfils the URL request with a virtual file or cached version of it. This is also the reason the final directives in a WordPress htaccess file must always be the WordPress rewrite rules. Ditto for other content management systems.

Once WordPress takes the URL, it processes the URL according to its own internal rewrite directives that are controlled by a PHP script.

If you’ve ever wondered how WordPress processes URLs, here is where your wondering ends: the process flows something like this:

  • A request is made to the server in the form of a URL.
  • The server processes the request according to global directives set in httpd.conf, virtual host files, php.ini and other configuration files.
  • Apache further processes the URL request as defined in htaccess files in every directory encounted while fetching the requested directory or document.
  • Apache reaches the WordPress site’s root htaccess file and eventually reads the WordPress rewrite rules.
  • WordPress takes over the URL and begins processing the request according to its own internal directives if the request is for a non real file.

If the URL request is for a “real” document or directory stored on the server then Apache serves that document or directory. If it’s not a real file then WordPress handles the URL and tries to serve a virtual document. If WordPress can’t find the document, a 404 error page is served.

We can limit the number of 404s by altering the URL before WordPress receives it, by serving replacement files when we know a missing requested file is a type that WordPress doesn’t serve as a virtual file, and by using WordPress to match requests against suitable alternatives to serve.

Disable any security plugins that are presently active in your WordPress site.

Activate the Permalink Finder plugin that you installed earlier. This plugin serves a suitable replacement file when the requested file cannot be found.

Open your WordPress site’s htaccess file. It will be the one in the site’s home directory that also stores the wp-config.php script. This directory is known as the site’s root directory. Make a backup of it because you need to delete most of its current content.

Except for the WordPress rewrite rules, remove any htaccess directives that are currently in your htaccess file. The WordPress rewrite rules are sandwiched between # BEGIN WordPress and # END WordPress.

Put the following directives into the htaccess file. Put them above the line that reads # BEGIN WordPress. Remember to keep a new line between these directives and # BEGIN WordPress:

Save the file. Close it. Reload your site.

Read the comments because they give further information about using the above directives properly. The final part replaces missing images with a one-size-fits-all stand-in. It requires you to upload an image to your server and place the path to the image in the rewrite rule. For example, if you use a file called dilbert.jpg in wp-content/uploads/dilbert.jpg then you would replace example/image.jpg with wp-content/uploads/dilbert.jpg.

Make sure the replacement file is not large in terms of data size. Try to keep it under 100kb.

Activate the Redirection plugin so you can monitor and fix 404s that the above methods do not resolve.

404s can be telltales signatures of attempts to hack a site. Consider the 404s you find and add originating IPs to your topmost htaccess file’s denial directives if they look like attempts to hack your site.

Part Five: Defend Against Query String Attacks and RFI Attacks

Activate the rest of the plugins we installed earlier. Do it in the following order:

  • Better WP Security

If you intend to change your admin username and database table prefix using Better WP Security, do so before activating the next two plugins.

  • Wordfence

The two other plugins (Redirection and Permalink Finder) should already be active.

Go through the settings for each plugin and configure them as suited to your environment and needs.

Both Wordfence and Better WP Security duplicate some features. Where features coincide, use the Wordfence features in preference to Better WP Security.

Better WP Security

Click the Security tab in your WordPress dashboard and follow its suggestions. It will write to your htaccess file. So when we next edit your htaccess file, make sure to reopen it for editing or you’ll end up overwriting vital security directives placed by Better WP Security.

It is usually safe to change your admin username and your WordPress database table prefix. I’ve made these changes to many mature sites and never had it go wrong. That doesn’t mean it can’t go wrong so back up your database before you change them.

You can change the name of your wp-content directory. This blocks most automated hacks that use bots to probe plugin and theme files in the wp-content directory for vulnerable files. Probing bots will not find what they are looking for if  wp-content doesn’t exist. It is good to change the name of wp-content but only do it when working on a fresh WordPress install with no media uploads or if you know how to edit the WordPress database to correct URL references.

Always rename the wp-content directory for fresh WP installs that have no posts, pages, internal links, uploads or other content added to them since installation.

Better WP Security makes all the required wp-config.php edits needed to rename the wp-content directory and it renames the directory but it doesn’t edit your database.

Renaming the wp-content directory of mature sites can be complicated. It breaks internal links within posts and post meta data. It breaks file hotlinks to images and downloads etc.. (might not be such a bad thing). If you rename the wp-content directory of a mature site you will need to edit your database to replace all instances of wp-content/ (that forward slash is important) with the new directory name e.g if you rename wp-content/ to file-uploads-xyz/ then you  would replace wp-content/ with file-uploads-xyz/. You must also be careful not to inadvertently give the game away by renaming any mentions of the old wp-content directory within posts and comments.

When editing the WordPress database, remember that entries in the wp_options table are in a format that states how many characters each entry has. WP ignores the entry when the number of characters do not match the number expected by WordPress.

When you rename the wp-content directory you must also edit any instances of wp-content within you site’s htaccess file to reflect the directory’s new name.

Disabling the theme and plugins editor in the backend can cause some plugins to stop functioning. Quick Cache is one of these plugins.

Wordfence

Wordfence is a newcomer to the WP security scene. It scans site files to check for malicious content, compares WordPress core files and plugins to their originals stored in the plugin repository and suggests fixes for faults discovered.

This plugin performs a few other security checks too. You can read more about  it at wordfence.com.

Wordfence has low impact on server resources. It uses an external server to perform some of its work.

If You Don’t Use Better WP Security to Rename wp-content

You can still protect your site from most automated probes of wp-content and against attempts to use query strings to upload files and inject code into the WordPress database through attempts to hack scripts in the wp-content directory.

Part of this is easy to  implement, part of this it tricky. How many of these snippets you use depends on what you use your site for, how often you intend to install plugins and how secure you want your site to be. It’s all explained in the small print.

All these snippets go above the line # BEGIN WordPress. They should be placed in the order they are listed. If you skip a block of directives, place the next block you use after the last block you used. Keep an empty line between each block to make it easier for you to read your htaccess file later.

How many themes do you actively use on your site?

General advice is to remove unused themes. Delete them. This is not always practical and often it’s a chore to reinstall a theme. Why delete it when you can simply block access to its directory. Indeed, reverse that notion: block access to all themes in the wp-content/themes/ directory but whitelist access to only the theme your site actively uses.

How many plugins do you actively use?

This one is a little more tricky. It requires you to white-list access to the directories of every plugin your site actively uses.

Just as done for the theme’s directory, add additional lines to the whitelist to permit access to your site’s active plugins only.

Block Query Strings

Ever seen a URL that has a question mark in it? Maybe one that looks like example.com/index.php?$login=true.

Anything after the question mark forms part of a query string. Query strings allow URLs to be used to give values to variables in scripts (a variable such as $login).

As with anything useful, query strings are vulnerable to misuse. Well written scripts and plugins sanitize query string values to mitigate hacking potential but even security minded script developers make mistakes.

You need to be very careful with this next set of directives. This set will prevent updates to plugins, themes and WordPress while-so-ever it is in your htaccess file. It will also stop some plugins from functioning properly. Despite the downside, they block query string attacks and are very, very useful for owners of sites that are left to run without much maintenance.

There are two forms of this directive set. The first is less stringent and will only block query strings. The second is extreme and removes anything after a question mark within a URL even if it is not a query string.

Both these methods require any plugins that use query strings during their functioning to be whitelisted. Use only one method.

Gentle Query String Stripper

Extreme Query String Stripper

An Alternative to the Above

A much less restrictive method to protect against RFI attacks is to use the following rewrite directives. Either use them instead of the two methods shown above or in synergy with them.

This is not the most recommendable method but it gets the job done in most cases. It works by blocking query stings and URI requests that refer to files stored on remote hosts that match commonly used hostnames such as picasa.com and blogger.com.

If you use Better WP Security to Rename WP-Content

Block attempts to probe the non existent wp-content directory. Some plugins require that the wp-content directory exists. They don’t use it but they look for it just the same. So recreate the directory wp-content and create two directories within it: one called themes and another called plugins. Add a blank htaccess file into each of those new directories. Now block client side access to the wp-content directory. Only do this if you’ve renamed your real wp-content directory.

I said it would be tricky. If you need help, post a request in the comments section or send me a private message with the contact form.

Part Five: If Registration is Disabled

Many bots and hackers attempt to create user accounts with WordPress blogs or to hack into WordPress sites through the signup, registration and login pages. I prefer to disable registration and completely deny access to the registration and signup scripts.

I use external authentication systems such as Disqus, OpenID, Livefyre and Social Login to enable commenting. I recommend you do likewise then use these directives to deny access to wp-signup.php and wp-register.php. If you want to be really secure, deny access to wp-login.php except from your own IP address/es.

Part Seven: General Protection

These htaccess directives protect your site from PHP fingerprinting, POST requests sent through proxies, bogus image file uploads and executable file uploads. Place them in your WordPress site’s root htaccess file. Again, place them above the line that reads # BEGIN WordPress.

The final part of the above snippet, moves uploaded files with the extensions specified in the second-to-last-line to a safe directory location mentioned in the final line. The files are renamed with the extension noexec. Create a directory within the directory of the htaccess file this directive is put into. Change the word “example” in the last two lines to the name of the directory you’ve just created. With this directive, attempts to upload executable scripts will result in the uploaded script being moved to that directory (theoretically).

Create a file called index.txt in the safe directory.

Now create an htaccess file with the following htaccess directives in the safe directory. These directives prevent scripts from being executed and ensure the file index.txt is loaded when someone tries to browse the directory.

FAQ

Why am I redirected to my home page when I run plugin, theme and WordPress updates?

Either you have set Firewall 2 to block http and https strings in query string parameters or you have used htaccess directives to block query strings. For either case, disable the htaccess query string denial directives and disable the Firewall 2 plugin while you update your site.

Why do I get a forbidden error when I run plugin, theme and WordPress updates?

See the above answer. It’s the same.

I’ve used Better WP Security to rename the wp-content directory. Now some of my plugins return 404 errors. How do I fix this?

There are three possible reasons and solutions for this:

  1. A plugin can be hard-coded to look for its files in the wp-content directory. You’ll need to manually edit the plugin’s files to correct the code. Better still, speak with the plugin’s developer.
  2. Better WP Security might not have been able to edit wp-config.php to tell WordPress about the directory name change. If your theme works and most other plugins work then this reason can be discounted. Otherwise, check the permissions of wp-config.php to ensure it is writable by the server (644).
  3. A plugin might store file location data in your site’s database. You’ll need to manually edit your database to change occurrences of wp-content/ to whatever-you-changed-the-directory-name-to/. Instructions for this are next.

Is there an easy way to edit references to wp-content/ in my database?

Yes there is.

  • Download InterconnecIt’s Database Search and Replace script (searchreplacedb2.php).
  • Upload it to your server.
  • Backup your database. I usually install the backup database and work on the backup.
  • Change the script’s name.
  • Run the script by browsing to it. For example, if you uploaded it to example.com and changed its name to 123.php then you would browse to example.com/123.php.
  • Follow the script’s prompts. Please pay attention to warnings and read the instructions.

A guide to exporting/backing up databases is here at JournalXtra. Read it to prevent heartache from bad backups.

I changed my database table prefix with Better WP Security. Now I’m unable to log into my WordPress dashboard. I get a “You  do not have sufficient permissions to access this page” error. How do I fix it?

I bet you received an error along the lines of “Could not create table” from Better WP Security. Here’s how to get your dashboard back:

  1. Make a note of the new table prefix e.g xyz_
  2. Open wp-config.php and find the line that reads “$table_prefix  =“. Confirm the database prefix here is the one Better WP Security has changed it to e.g xyz_ so $table_prefix  = ‘xyz_';
  3. Still in wp-config.php, make a note of your site’s database table name. It is shown in the line define(‘DB_NAME’, ‘table_name’); where table_name will be your table’s name.
  4. Open phpMyAdmin, select the database used by your site e.g table_name. Scroll to the bottom of the window pane that displays  the tables, click “check all” then use the dropdown box to select “Optimize”. Repeat but this time use the dropdown box to select “Repair”. Click the Export tab, click Custom, select all tables, select Replace, then export the database. More detailed instructions are available here.
  5. When your table has downloaded, open it in a text editor, find and replace instances of wp_ with your new database table prefix e.g xyz_.
  6. Use phpMyAdmin to import the edited database into your site’s database tables. It should replace the old ones.
  7. Log back into your site.

Your Turn

Do you agree or disagree with any of this post? Do you have additional WordPress security tips? We’d love to hear from you. Use the comment form to express yourself.

Comments

    • says

       Better WP Security provides options that Secure WordPress doesn’t. BWP lets you change the name of the wp-content directory, enable SSL site wide (if available), change the login, register and sign-up slugs and make several other changes too. Some of those changes can be made through wp-config edits, which is how BWP does it. I highly recommend BWP..

      I use and recommend Secure WordPress just in case… You could go without it.

  1. Stephaniliffe says

    I have done most of the stuff above but I receive “Waiting for error404…” when loading any page of my site. Is this a problem. The page still loads but the 404 error worries me. at bigtimerusher.com

    • says

      Hi Stephani, thank you for keeping us updated. Could you let me know where the issue was and how you solved it so others can benefit from it in the future.

  2. says

    This is a great post thank you. I’m always out looking for ways to better my site security. Have you seen this new Plugin Wordfence Security? What do you think about it?

    Regards
    Hanè

  3. Tereza1159 says

    Thanks for sharing.  You can not have enough security nowadays.  I am definitely trying your suggestions.  RE the WP File Monitor Plus, I have looked everywhere for an answer, tried both the plus and original, my wp is in root (with other directories), but I can not get it to work.  It works in a directory by itself but not in root.  Current default path is home no forward slash.  Would appreciate any comments.

    http://jobs-onlineresources.info

    • says

      Hi Tereza. I need to update the post. Use the security plugin called Wordfence instead of WordPress File Monitor.

      Wordfence is in active development and can be set to scan just the site’s directories or all directories under the domain’s server space. It’s my guess that WP File monitor is trying to scan all files in all directories at great RAM usage which is causing your server to stop the scan. Wordfence can be configured to not use more than a set amount of RAM to prevent resource hogging; this can prevent timeouts.

    • says

      Hi Lee,  Thank you for the reply.  I will try Woodfence, thank you.  My system seemed to be doing exactly what you described.  You are a gem and much appreciated.  Tereza

    • Mark Maunder says

      Hi guys. I’m the creator of Wordfence. Great to hear your positive feedback. Please let me know if there’s anything I can do to improve our product. We’re doing a major release in the next 24 hours that will speed things up even more, particularly on dreamhost. My personal email is mark@wordfence.com. Our forums are at http://www.wordfence.com/forums/

    • says

      Hi Mark, thank you for coming by to say hello. I like Wordfence a lot and will definitely be updating my security related posts to include Wordfence in them.

      In case anyone doesn’t know, Mark is the guy who discovered a flaw in the old timthumb script was being used to hack WordPress sites. All of us in the WordPress community owe Mark a lot of gratitude for publicizing the hack and making timthumb safe again by rewriting it. That’s a big thank you from me, Mark.

    • says

      Thank you Tereza, you are welcome. You can get Wordfence by typing it into the Add New plugin part of your WP dashboard or by visiting wordfence.com.

  4. says

    Easier if I can access the site. Email me at leehodson (at) vizred.com and we’ll take it from there. generally, I now rely on Better WP Security and Wordfence. Spam Free WordPress is a good plugin for spam prevention. Better WP and Wordfence need to be configured correctly or they can cause issues with a site. Email me.

  5. Ken Miller says

    Hey Lee, this post is great. I appreciate how detailed these instructions are. The details are more helpful than most other security related articles I have found.

  6. says

    Great article can prove useful, just an important note about eh rfi attack and query string stripping, you need to be very careful this can affect and will affect admin features of wordpress like i experienced, given i had the strict extreme feature set.

    You will find that BULK deleting or filtering causes 404 errors.. not sure if there is a way to allow query strings that come from any wordpress admin request ?

Leave a Reply