Using .htaccess Files To Secure WordPress

.htaccess files are configuration files that can be used to Secure WordPress against hacking and spam. This article shows you how to do this.

Please read all of this article before you complete this step.

In This Article

Introduction

.htaccess files are configuration files used by the Apache Web Server.

A Web Server is the program that is responsible for taking a request for a page on your website and returning the correct page to the visitor.

Apache is the most commonly used Web Server in the world, and if you are on a shared hosting account you are most likely using an Apache Web Server. The Web Server from Microsoft is called IIS (Internet Information Services) and does not use .htaccess files.

.htaccess files are extremely powerful.

Modifications to your .htaccess file is an effective way of hacking your WordPress site.

For instance a hacker can re-direct all incoming traffic to your website to another website effectively stealing your traffic.

Some hacks redirect only incoming traffic from search engines. This means that if you open your WordPress site by typing the URL or via a bookmark you will see your site working normally. Only users who found your site via search will be redirected. Very sneaky!

We think it is very important that you are familiar with your .htaccess file. Otherwise it is very difficult to detect a hack in this file.

Adding security measures in the .htaccess file is very powerful, because the security screening takes place before WordPress even sees the request. This saves a lot of work for WordPress.

We highly recommend that you change the .htaccess file manually.

The changes are not too complicated. You do not need to understand exactly how the code works. Just get a good feel for what the file looks like and get the job done!

The steps to change the .htaccess files manually are described in detail in How You Complete This Security Checkpoint – Manual Changes on page 85.

If you are not comfortable making manual changes to your .htaccess files it is much better if you use the plugin instead of doing nothing.

Or you can use our services.

The AskApache Password Protect plugin is described in How You Complete This Security Checkpoint – Using Plugin on page 95.

stopImportant! Even a small typing mistake in the .htaccess file can stop your WordPress site from working.

If anything does go wrong you can restore access to your site by restoring the original .htaccess file.

Copy the original .htaccess file to your computer before you start editing.

If you are using the Ask Apache plugin you also need to backup your original .htaccess file. Just in case!

Important! When you have completed work on the .htaccess files please ensure you test your WordPress site comprehensively. Especially your contact form, comment posting and any other user input on your site needs to be tested.

 

Securing Your .htaccess File Manually

We will secure the WordPres root folder and the wp-admin folder in this section.

WordPress root folder

Backup The Current .htaccess File

Copy the .htaccess file from your WordPress root folder to your local computer and give it a name like htaccess_wordpress_root_original_TODAYSDATE.txt.

Update The .htaccess File

Before you start your .htaccess file should look similar to this:

[gn_note color="#ffffff"]# BEGIN Cache Plugin
...

# END Cache Plugin


# BEGIN WordPress

<IfModule mod_rewrite.c>

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
[/gn_note]

Don’t make changes to the code already in the file.

The code for Cache Plugin will only be present if you use a caching plugin like WP Super Cache or W3 Total Cache.

We will add a few pieces of code marked in bold – explanation follows.

Add the lines to the beginning of the .htaccess file.

[gn_note color="#ffffff"]# #############################################
# BEGIN WordPress Security Checklist Addition

# #############################################


# disable directory browsing

# Note! Bluehost changes this line automatically

Options All -Indexes


# Stop access to sensitive files

# Protect .htaccess

<Files .htaccess>

Order Allow,Deny

Deny from all

Satisfy all

</Files>
 

# Protect readme.html

<Files readme.html>

Order Allow,Deny

Deny from all

Satisfy all

</Files>


# Protect wp-config.php

<files wp-config.php>

Order Allow,Deny

Deny from all

Satisfy all

</files>


# Protect php.ini

<files php.ini>

Order Allow,Deny

Deny from all

Satisfy all

</files>
 

# Protect error_log

<filesMatch "^(error_log)$">

Order Allow,Deny

Deny from all

Satisfy all

</filesMatch>


# Block the include-only files

RewriteEngine On

RewriteBase /

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]
 

# ###########################################

# END WordPress Security Checklist Addition

# ###########################################


# BEGIN Cache Plugin

...

# END Cache Plugin


# BEGIN WordPress

<IfModule mod_rewrite.c>

RewriteEngine On

RewriteBase /

RewriteRule ^index\.php$ - [L]

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . /index.php [L]

</IfModule>

# END WordPress
[/gn_note]
# disable directory browsing

This directive tells the Apache web server to dis-allow directory browsing.

For instance if you go to www.mysite.com/wp-content/uploads you will likely see a result like this:
unprotected uploads folder

We do not want our visitors to get this much information about our site.

Now they will see this:
protected uploads folder

Much better!

# Stop access to sensitive files

We protect a number of files with sensitive information from access from the Internet. These files might expose information such as WordPress version number, server configuration and database prefixes etc.

  • .htaccess – contains sensitive information about the server configuration and protection.
  • readme.html – contains the WordPress version number.
  • wp-config.php – contains information about your database name, user, password and table prefix.
  • php.ini – contains sensitive information about the server configuration and protection.
  • error_log – can contain information about your file paths and database table names.

We don’t want to give our visitors that type of information.

This is what we will give them:
permission denied

# Block the include-only files

This section blocks access to files that no-one should be accessing directly from the Internet anyway. They should only be called from other files on our WordPress site.

Test And Backup

Now is a good time to test that your site is still working as it should… especially the parts with user input…

When you have verified everything works copy the updated .htaccess file to your local computer with a name like htaccess_wordpress_root_extra_secure_TODAYSDATE.txt.

The Perishable Press 5G Blacklist – web page

The Perishable Press 5G Blacklist is a block of code you add to the .htaccess file. It is very effective at blocking unwanted visitors to your website.

This checkpoint is where you are most likely to see your WordPress site break. If this happens follow these steps:

  • Remove all of the 5G code.
  • Test to see that your site works again.
  • Add the first half of the code.
  • Test to see if your site works.
    • If it does work add the second half of the code.
    • If it does not work remove half of the added code and test again.
    • Etc.

By following this method you can quickly determine exactly which line is causing problems. You can then leave out that line. And remember there might be more than one line causing problems.

For more details on this method read this article on our website.

The more code you leave in the better you are protected. However your site needs to work!

Remember, test thoroughly.

Add this code to the beginning of your .htaccess file:

[gn_note color="#ffffff"]# ############################################
# BEGIN 5G BLACKLIST/FIREWALL

# @ http://perishablepress.com/5g-blacklist/

# ############################################


# 5G:[QUERY STRINGS]

<ifModule mod_rewrite.c>

RewriteEngine On

RewriteBase /

RewriteCond %{QUERY_STRING} (environ|localhost|mosconfig|scanner) [NC,OR]

RewriteCond %{QUERY_STRING} (menu|mod|path|tag)\=\.?/? [NC,OR]

RewriteCond %{QUERY_STRING} boot\.ini [NC,OR]

RewriteCond %{QUERY_STRING} echo.*kae [NC,OR]

RewriteCond %{QUERY_STRING} etc/passwd [NC,OR]

RewriteCond %{QUERY_STRING} \=\\%27$ [NC,OR]

RewriteCond %{QUERY_STRING} \=\\\'$ [NC,OR]

RewriteCond %{QUERY_STRING} \.\./ [NC,OR]

RewriteCond %{QUERY_STRING} \? [NC,OR]

RewriteCond %{QUERY_STRING} \: [NC,OR]

RewriteCond %{QUERY_STRING} \[ [NC,OR]

RewriteCond %{QUERY_STRING} \] [NC]

RewriteRule .* - [F]

</ifModule>


# 5G:[USER AGENTS]

<ifModule mod_setenvif.c>

# Next line has been commented out to allow for Google +1 to show images

# SetEnvIfNoCase User-Agent ^$ keep_out

SetEnvIfNoCase User-Agent (casper|cmsworldmap|diavol|dotbot) keep_out

SetEnvIfNoCase User-Agent (flicky|ia_archiver|jakarta|kmccrew) keep_out

SetEnvIfNoCase User-Agent (libwww|planetwork|pycurl|skygrid) keep_out

SetEnvIfNoCase User-Agent (purebot|comodo|feedfinder|turnit) keep_out

SetEnvIfNoCase User-Agent (zmeu|nutch|vikspider|binlar|sucker) keep_out

<limit GET POST PUT>

Order Allow,Deny

Allow from all

Deny from env=keep_out

</limit>

</ifModule>


# 5G:[REQUEST STRINGS]

<ifModule mod_alias.c>

RedirectMatch 403 (https?|ftp|php)\://

RedirectMatch 403 /(cgi|https?|ima|ucp)/

RedirectMatch 403 /(Permanent|Better)$

RedirectMatch 403 (\=\\\'|\=\\%27|/\\\'/?|\)\.css\()$

# Next line has been modified. Second test ('//') has been removed

# to allow performance testing using gtmetrix.com and Pingdom FPT

#RedirectMatch 403 (\,|//|\)\+|/\,/|\{0\}|\(/\(|\.\.\.|\+\+\+|\||\\\"\\\")

RedirectMatch 403 (\,|\)\+|/\,/|\{0\}|\(/\(|\.\.\.|\+\+\+|\||\\\"\\\")

RedirectMatch 403 \.(cgi|asp|aspx|cfg|dll|exe|jsp|mdb|sql|ini|rar)$

RedirectMatch 403 /(contac|fpw|install|pingserver|register)\.php$

RedirectMatch 403 (base64|crossdomain|localhost|wwwroot|e107\_)

RedirectMatch 403 (eval\(|\_vti\_|\(null\)|echo.*kae|config\.xml)

RedirectMatch 403 \.well\-known/host\-meta

RedirectMatch 403 /function\.array\-rand

RedirectMatch 403 \)\;\$\(this\)\.html\(

RedirectMatch 403 proc/self/environ

RedirectMatch 403 msnbot\.htm\)\.\_

RedirectMatch 403 /ref\.outcontrol

RedirectMatch 403 com\_cropimage

RedirectMatch 403 indonesia\.htm

RedirectMatch 403 \{\$itemURL\}

RedirectMatch 403 function\(\)

RedirectMatch 403 labels\.rdf

RedirectMatch 403 /playing.php

RedirectMatch 403 muieblackcat

</ifModule>


# 5G:[BAD IPS]

<limit GET POST PUT>

Order Allow,Deny

Allow from all

# uncomment/edit/repeat next line to block IPs

# Deny from 123.456.789

</limit>


# ############################################

# END 5G BLACKLIST/FIREWALL

# @ http://perishablepress.com/5g-blacklist/

# ############################################
[/gn_note]
Test And Backup Again

Now is a good time to test that your site is still working as it should…

When you have verified everything works copy the updated .htaccess file to your local computer with a name like htaccess_wordpress_root_extra_secure_5G_TODAYSDATE.txt.

WordPress wp-admin folder

We will now be working with the .htaccess file in the wp-admin folder.

The goal here is to limit access to the WordPress administration panel.

There are two ways of doing this:

  1. Limit access to approved ip addresses only.
    Plus: Very secure. Only computers on specific ip addresses are allowed.
    Minus: Does not work well if you work from changing locations or if you do not have a fixed ip address.

  2. Password protect the wp-admin folder.
    Plus: Flexible with added protection. Can work from anywhere.
    Minus: Not as secure. A password can potentially be broken.

You only need to select one method.

If you only ever work with your WordPress administration panel from a few locations with fixed ip addresses we recommend option 1. Otherwise option 2 will work just fine.

Although not quite as secure as option 1 the fact is that most hacking is done by automatic robots working their way through the Internet. They are not particularly intelligent, and if they get turned away from the wp-admin folder they will simply move on to an easier target.

Limit access to approved ip addresses only

Important! We are now working on the .htaccess file in the wp-admin folder!

If the file does not already exist create it.

If it does exist copy the file to your local computer and give it a name like htaccess_wpadmin_original_TODAYSDATE.txt

Add the following code:

[gn_note color="#ffffff"]# #############################################
# BEGIN WordPress Security Checklist Addition

# #############################################


AuthUserFile /dev/null

AuthGroupFile /dev/null

AuthName “WordPress Admin Access Control”

AuthType Basic

<LIMIT GET>

order deny,allow

deny from all

allow from xxx.xx.xx.xxx

</LIMIT>


# ###########################################

# END WordPress Security Checklist Addition

# ###########################################
[/gn_note]

Add your IP address in the allow from line. You can add as many allow from lines as you wish.

Tip! To find your current IP address click here.
your ip address

Tip! If you do need to access your WordPress administration panel from an unlisted ip address you can rename the .htaccess file to .htaccess.bak while you need access… then change the name back when you are done.

Note! As mentioned above this restriction only works well if you work from a limited number of fixed ip addresses. Otherwise we recommend you use the password protection described below.

Password protect the wp-admin folder

If your WordPress site is on a shared hosting plan you can most likely password protect the wp-admin folder from the cpanel or the File Manager. If you are in doubt ask your host.

This will create the .htaccess file for you and store the user name and password safely.

On Bluehost you select Password Protect Directories in the cpanel.
password protect folders

On Godaddy you need to use the File Manager.
godaddy password protection

If you are on a dedicated server you can find instructions for password protecting a folder here.

Once the wp-admin folder has been password protected you will get this prompt when you access www.mysite.com/wp-admin.
password prompt

We need to allow access to a few files in the wp-admin folder without requiring a password.

Open the file and add the lines in bold.

[gn_note color="#ffffff"]AuthType Basic 
AuthName "Restricted Access"
AuthUserFile "/xxxxxxxxx/passwd"
require valid-user
 

# #############################################
# BEGIN WordPress Security Checklist Addition
# #############################################

<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|mp3|mpg|mp4|mov|wav|wmv|png|gif|
swf|css|js)$">
Allow from All
</FilesMatch>

<FilesMatch "(async-upload|admin-ajax)\.php$">
Order allow,deny
Allow from all
Satisfy any
</FilesMatch>

# ###########################################
# END WordPress Security Checklist Addition
# ###########################################

[/gn_note]

Save the file and test that you get the password challenge when you access the WordPress administration panel.

Securing Your .htaccess File Using Plugin

Backup The Current .htaccess File(s)

Copy the .htaccess file from your WordPress root folder to your local computer and give it a name like htaccess_wordpress_root_original_TODAYSDATE.txt.

If you already have an .htaccess file in the wp-admin folder copy it to your local computer and give it a name like htaccess_wpadmin_original_TODAYSDATE.txt

Important! If you experience any problems using the Ask Apache plugin you can restore access to your WordPress site by restoring these two files… if you did not have an .htaccess file in the wp-admin folder you need to delete the file created by the plugin in case of problems.

Install And Configure The AskApache Password Protect Plugin

Follow these steps:

  • Add and Activate the AskApache Password Protect plugin.

  • Read the warning, and understand the procedure for restoring your site in case anything goes wrong.
    ask apache warning

  • Scroll to the bottom and click Initiate Tests.
    initiate tests

  • On the Test Results page investigate any items that are not green to see if they are required or not. If you find any problems prohibiting you from running the plugin you can always make the changes manually as described above.

  • Scroll to the bottom and click Continue to Setup.
    continue to setup

  • Enter the user information to protect the wp-admin folder.
    user info for password protection

  • For the Authentication Settings make sure you select a location for the password file which is not under your public_html folder.
    select location

    If required create a folder at the same level as public_html.
    create folder

  • The default values should be fine for the remaining fields.

  • Click Save Settings.
    save settings

  • For Password Protect wp-admin click Activate.
    activate password protection

  • For Directory Protection click Activate.
    activate directory protection

  • We recommend that you do not activate the WordPress Exploit items.
    dont activate exploit protection

Test And Backup

Now is a good time to test that your site is still working as it should… especially the parts with user input…

When you have verified everything works copy the updated .htaccess files to your local computer with names like htaccess_wordpress_root_extra_secure_TODAYSDATE.txt and htaccess_wp-admin_extra_secure_TODAYSDATE.txt..

Optional

You can activate other modules to help prevent spam and other exploits.

Recommendation

When you have made the required changes to the .htaccess files we recommend that you make a backup of the files.

You should also change permissions on all .htaccess files to 444. Changing permissions has successfully stopped some – but not all – attempts at modifying the .htaccess file.

In FileZilla right click on the file and enter 444.
change file permissions

And we strongly recommend that you thoroughly test all aspects of your site still work.

Further Resources

whiterabbitFollow The White Rabbit

[gn_spoiler title="Click Here" open="0" style="1"]Are you reading this article as a part of the Interactive Version of The WordPress Security Checklist?

Then you can find your next article below.

If not you should take a look at the Table Of Contents.

Next article: Securing PHP
Previous article: Intrusion Detection Systems[/gn_spoiler]

Questions Or Comments?

Please leave them below. Thanks!


About Anders Vinther

Anders is on a mission to make it easy for you to secure your WordPress.

Let's make it harder for the bad guys!

Want More?

Sign up for our newsletter and we'll let you know when we have got new stuff about WordPress Security for you. See past emails.


Most Popular Articles – All Time

Most Popular Articles – This Week

Comments

  1. When I try to run the Ask Apache Password Protect plug-in, I get the following error message, “edit_files cap required” and nothing else happens. What do I need to change or do differently in order to get the plug-in to work?

  2. Adding Options All -Indexes in .htaccess at the root completely disables the site (500 error). Has the behavior changed with recent version of WordPress?

    • Hi Xavier,

      An 500 error in this case could indicate that you’ve made a syntax error in the .htaccess file.

      Take out the additions you’ve made, check in works again, then double check the stuff you have added for typos.

Speak Your Mind

   Login Using:

*

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax