The most essential .htaccess rules for blogs

This article is part of a blog optimization process I've implemented lately and taken the loading times of my blog from 12-18 seconds to 4-7 2-3 seconds. As a bonus, these .htaccess rules improved the blog security at the same time.

Here's the thing. Many blogs have a .htaccess file that only has the rules WordPress (or any other blogging platform) install places there and possibly rules a plugin has created too. If you don't add these .htaccess rules to your root .htaccess file, you're missing out easy performance improvements any blogger can have. So don't count on your hosting provider's default rules (if there even is any) and make your blog better with these simple .htaccess rules.

As a part of general optimizing a blog for both fast page loading and securing the site, the .htaccess -file at the root of your domain is an easy way to do web page optimization on a site level. Putting these rules into use won't alone cut your page loading times to half, but they do provide the foundation for further optimization and speed improvements for your blog, or any website for that matter.

You can find the most essential .htaccess blog optimization and security additions from this post. After reading this article you know how to

  • protect the .htaccess file itself
  • protect your core blog files, like wp-config.php on WordPress
  • prevent anyone from seeing the directory indexes on your server
  • protect your blog from direct comment spam
  • setup file compression to speed up the blog
  • (optionally) prevent people from (hot)linking to images on your blog
  • configure browser cache setting for your files, so your returning visitors can enjoy super-fast loading through their browser cache

.htaccess Basics

.htaccess (HyperText ACCESS) is the default name of a directory-level configuration file that allows for decentralized management of web server configuration.

Originally the file was designed to control directory level access, e.g. password-protect a directory, but .htaccess is also a way to change how the webserver works. The .htaccess directives affect the directory the file is in and all sub-directories under it, thus the .htaccess rules at the root of your domain controls every directory and request that happens on your server.


Before You Start Adding and Changing .htaccess Rules

Adding .htaccess rules, optimizing your blog and making it secure is easy when you know what to do, but because erroneous rules in the root .htaccess can really mess a site up, here's some words of warning (don't be too worries thou, this is quite simple)

  1. Always backup your existing .htaccess file!
  2. Keep that original in a safe place and don't overwrite it.
  3. .htaccess is case-sensitive and incorrectly spelled code will cause errors on your server.
  4. Make sure you edit YOURDOMAIN name where applicable.
  5. After editing and saving the new version of .htaccess, test that everything works as intended and
  6. if you're technologically savvy-enough, check your servers error logs to make sure everything is OK.
  7. If something is not working, delete the rule you added the last or revert to your backed-up .htaccess file.

Blog Optimization and Security Using .htaccess File

Apply these rules to the root .htaccess so it will affect all (sub-)directories on your server. If you have access to httpd.conf that is even better, as rules in that file are set once on the server startup and not checked per request like they are with .htaccess although httpd.conf is not suitable for all the rules here.

1. Protect .htaccess From Outside Access

This should be at the start of each and every root .htaccess file you ever create.

# Protect the .htaccess file
<Files .htaccess>
Order Allow,Deny
Deny from all

Source: Perishable Press: Stupid htaccess Tricks

2. Protect wp-config.php From Unwanted Access

Same principle as above, apply the same principle to protect any file, but these two are the most important files to protect

# Protect wpconfig.php
<Files wp-config.php>
Order Allow,Deny
Deny from all

Not that this rule can be in .htaccess file at the same directory as the protected file, so not necessarily the root .htaccess

Source: Perishable Press: Stupid htaccess Tricks

3. Disable Directory Browsing

This simple directive will prevent anyone from seeing the index and files in your directories. Your file permissions are important too, but this will prevent a casual visitor from seeing the index and the files in any directory on your server.

# Disable directory browsing
Options All -Indexes

Source: Perishable Press: Stupid htaccess Tricks

4. Protect From Spam Comments

Comment spammers can hit your comment script directly and this rule will prevent anyone from leaving comments without using the comment form. Spam blogging plugins catch the spammers, but as this is the most economical way to prevent direct comment spam attempts, it should be in the .htaccess-file of any blog.

# Protect from spam comments
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} .wp-comments-post\.php*
RewriteCond %{HTTP_REFERER} !.** [OR]
RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule (.*) ^http://%{REMOTE_ADDR}/$ [R=301,L]

This code is created for WordPress blog (comments left with the wp-comments-post script. If the comment script is not referred from YOURDOMAIN (edit your domain there, without the www.) or there is not user agent, the request is redirected back to the referrer.

In very rare cases, some browser might have a blank user agent and this code might block those browsers as well.

Source: WPRecipes -- How to: Deny comment posting to no referrer requests

5. (OPTIONAL) Prevent Hotlinking

If someone is stealing your bandwidth and hotlinks to images on your server, this will help. It will redirect any outside linking to an image on your server to another image instead which you can have on another (free) domain.

Make a "please don't hotlink" image and save it on a free image hosting, like Flickr, Picasa or such and edit the URL of the image to the code (replacing with the URL to the picture).

# Protect bandwidth
<IfModule mod_rewrite.c>
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(.+\.)?YOURDOMAIN\.com/ [NC]
RewriteRule .(jpg|jpeg|png|gif)$ [NC,R,L]

Do not use this code unless you know what you're doing. This will prevent using the images on your server from any other servers. Unless you do some tweaks, this will prevent showing the images in RSS readers, like Google Reader, so be aware of that.

Personally, I'm not using the hotlinking-prevention at the moment and it's not necessary unless you start to have problems with hotlinking.

Source: Girl Geekette: .htaccess – How To Stop Hotlinking

6. Compress Content

This next code utilizes Apache's mod_gzip and newer mod_deflate code to compress files on the fly. Most, if not all modern browsers can process compressed content and get it uncompressed on the fly, thus speeding up loading the site for the user, saving your bandwidth and reducing server load.

As compressed files are smaller, less data must be downloaded to your readers browser and less data travels faster. Everyone wins :)

6.1. Test What Apache mod_ Is Active On Your Server

Your server probably has mod_gzip (Apache 1.3) or mod_deflate (Apache 2.x, preferred!) active. To test which you can / should use, here's a simple test you can use. As it works for other Apache mods as well, we can test if your server has mod_headers and mod_expires active as well (for setting headers and expiration times below).

1. Create index.shtml.txt file with one line (#printenv inside a html comment):

<!--#printenv -->

2. Create .htaccess file with these lines:

SetEnv MOD_mod_deflate 0
SetEnv MOD_mod_gzip 0
SetEnv MOD_mod_headers 0
SetEnv MOD_mod_expires 0
<IfModule mod_deflate.c>
SetEnv MOD_mod_deflate 1
<IfModule mod_gzip.c>
SetEnv MOD_mod_gzip 1
<IfModule mod_headers.c>
SetEnv MOD_mod_headers 1
<IfModule mod_expires.c>
SetEnv MOD_mod_expires 1

3. Create a temporary directory to your server, e.g.

4. Transfer the index.shtml and .htaccess you just created to the temp-directory

5. Access the index.shtml.txt, e.g. (if you have problems opening the index.shtml.txt, rename the filed as index.shtml and access the directory, e.g.

6. You are looking to find either MOD_mod_deflate=1 OR MOD_mod_gzip=1 (and MOD_mod_headers=1 + MOD_mod_expires=1) on the page. 1 means the module is enabled. 0 means the module is not enabled.

7. Delete the temporary directory

After testing which mod is active, use Either mod_gzip or mod_deflate compression in the .htaccess:

Source: DreamHost Wiki: Apache HTTP Server

6.2. Using mod_gzip Compression

# mod_gzip compression (legacy, Apache 1.3)
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file \.(html?|xml|txt|css|js)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*

Source: SAMAXES: .htaccess - gzip and cache your site for faster loading and bandwidth saving

6.3. Using mod_deflate Compression

# BEGIN Compression (DEFLATE)
<IfModule mod_deflate.c>
# Enable compression
AddOutputFilterByType DEFLATE text/css text/javascript application/x-javascript text/html text/plain text/xml image/x-icon
<IfModule mod_setenvif.c>
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
<IfModule mod_headers.c>
# Make sure proxies deliver correct content
Header append Vary User-Agent env=!dont-vary
# Ensure proxies deliver compressed content correctly
Header append Vary Accept-Encoding
# END Compression (DEFLATE)

NOTE: All AddOutputFilterByType rule should be on one line (next line always starts with the comment # Dont' compress...) , even if it wraps above.

Source: Apache HTTP Server documentation: mod_deflate (I optimized the rules, this version of the directives is also in the post: .htaccess rules for site speed.)

6.4. Test That Compression Is Working

To test if compression is working on a specific file or URL, you can check it at:

As a bonus, you also see how much data transfer compression is saving you (and your readers) on that document.

7. Set Expire Headers and Times

Static content, like images, can be cached to your visitors browser, but unless you set the expiration times, there is no way for the browser to know if the component is still valid or has it expired. Thus all files are loaded from your server, putting load on your blog and slowing the loading speed for the reader.

There are two ways to set these. By setting a expiration time per file type or setting expiration headers. The times are configured in seconds (e.g. one hour = 3600 seconds) or through natural language. When considering the expiration times, think how often the content is updating.

For example, if you upload an image, there's a change that you won't ever change or edit that image, thus images can have long expiration times (1 month or over an year). Then again, the html- and php-files on your blog are quite dynamic, so it's probably good to keep the expiration times under 2 hours (or even set the expiration times to 1 sec or completely off).

7.1. Disable ETags

It would be beneficial to use ETags as an accurate way for server and browser to communicate and see whether or not a file has been updated/expired, but because it's very likely that the ETags are not working properly, it's better to just disable them (and use "last-modified" instead):

# No ETags, No Pragma
<IfModule mod_headers.c>
Header unset Pragma
Header unset ETag
FileETag none

7.2. Set Expiration Times For Caching

Choose one of the options, depending if you have mod_expires, mod_headers or both enabled. These are example cache header and expiration time configurations you might use. Feel free to tweak your own using these as a guideline.

7.2.1. mod_expires & mod_headers

Cache settings from my speed-optimized .htaccess rules:

<IfModule mod_headers.c>
# Default cache time to 1 year (31536000 sec)
Header set Cache-Control "max-age=31536000, public, must-revalidate"
<IfModule mod_expires.c>
# Turn on Expires
ExpiresActive On
# set default to "access plus 1 year"
ExpiresDefault A31536000
# html - "modification plus 1 hour"
ExpiresByType text/html M3600
# css and JavaScript - "modification plus 6 weeks"
ExpiresByType text/css M3628800
ExpiresByType text/javascript M3628800
ExpiresByType application/x-javascript M3628800
# No cache for php-files
<FilesMatch "\.(php)$">
<IfModule mod_expires.c>
ExpiresActive Off
<IfModule mod_headers.c>
Header set Cache-Control "private, no-cache, no-store, proxy-revalidate, no-transform"

Source, inspiration & references:

7.2.2. What Expires or max-age time to use?

Some set HTML expiration times to 1 second, as blog's are dynamic with comments and all, and others like to use 1-2 hour cache to fasten the loading for the returning visitors. Personally, I use 1 hour (3600 seconds) at the moment. If you have a static site and the HTML pages are not changing much, set HTML caching to 1 month or even 1 year (never expires).

If you're using plugin like WP-Super Cache, the plugin sets compression and cache rules for the .html(.gz) pages it creates, so you might want to make your own rules match those (WP-Super Cache uses mod_deflate and 5 minutes / 300 seconds for the html pages).

Also, if you set long expiration times for CSS-files and you do edit them, e.g. your blog's theme, you will have to change the file name or just settle for shorter expiration times. For example, WordPress themes are using style.css by default and the name of it is not changing, but you can create non-theme related stylesheet where you import or copy the contents of the different stylesheet.

7.3. Alternate Way, Using ETags

If you use this, don't disable ETags as shown at 7.1.

FileETag MTime Size
<IfModule mod_expires.c>
<FilesMatch "\.(jpg|jpeg|gif|png|css|js)$">
ExpiresActive on
ExpiresDefault "access plus 1 year"

Source: The Ultimate WordPress 2.8 Optimization Guide

7.4. (Extra) Use Minimal Caching During Maintenance

Very useful when you're doing changes to your site and want to see the changes without doing a forced reload everytime.

# implement minimal caching during site development
<FilesMatch "\.(flv|gif|jpg|jpeg|png|ico|js|css|pdf|swf|html|htm|txt)$">
<IfModule mod_headers.c>
Header set Cache-Control "max-age=5"
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault A5

Inspired by: Perishable Press: Stupid htaccess Tricks

8. Additional .htaccess Tricks

These are not related to optimization or security and you shouldn't use these unless you have a reason and know what you're doing.

8.1. Your Own Shortlinks (in WordPress)

Even if you're using SEO-friendly permalink (like you should), the WordPress default url for posts and pages is still active, e.g.

You can use that to your advantage and use this directive to remove the need for that ?p= in the url and have your own short-URLs, like for example this post can be found as

# BEGIN URL Shortening
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/([0-9]+)$
RewriteRule .* [R=301,L]
# END URL Shortening

This is more useful if you have a short domain name, and even then a dedicated URL shortening service is better as it provides tracking and such, but it's still a cool trick that you can use to have your domain showing in Twitter.

Source: How To Create Your Own Tiny URL

8.2. Force Download (e.g. for mp3, PDFs, etc.)

If you're offering downloads from your blog, you might want to save the users for the usual "Right-click + Save As.." with this rule for the relevant files. This rule will force the files to be downloaded instead of opening them in a browser or other linked program.

<FilesMatch "\.(mov|mp3|pdf)$">
ForceType application/octet-stream
Header set Content-Disposition attachment

Source: Ultimate htaccess Tutorial for .htaccess files

9. References And Further Reading

There's a lot you can do with .htaccess and server rules, but the ones shown on this article are the most essential and you will do fine by just using these, but in case you want to go all out with more .htaccess tricks, I recommend you check these two documents (already referenced multiple times above):

Nothing stupid on this one tbh! If you want to protect a directory with a password, do fancy redirection, block IP-addresses and bots, limit your Admin-area to only your IP and much more, it's all there!

There are many guides and tutorial that call themselves "ultimate" without being one. This one is called ultimate for a good reason.

That's It! Go Tweak Your .htaccess

Good hosting services take care of default .htaccess for you, but some hosting providers don't automatically add a .htaccess file to your server. The only thing you will see in the default .htaccess file (if there even is one) are the settings added there by the blog-installer or some plugin (like WP-Super Cache).

Now you know how to change this! With these tips you can accomplish two important things, higher security and bit of optimization.

If .htaccess file is not there, just create a htaccess.txt file (as .htaccess might be hidden in your operating systems) on your local computer, FTP it to the server root and rename it to .htaccess

There are a lot of things you can do with .htaccess, like various redirects and blogging users (spammers) based on IP-address or a spam-bot they use, but for most bloggers, the tweaks on this post will do just fine.

If you're not confortable editing the .htaccess yourself, get someone to help you (you can even ask me!). It's not hard, as long as you follow the instructions and use the code exactly as it is. And as you did backup your original .htaccess, you can always go back to that.

As said, these .htaccess rules are a part of my blog optimization process, which I have implemented to make this blog load 2-4 times faster than before (yeah, I know, it REALLY sucked before), so there's more to come -- But now if you have any suggestions, questions or feedback, leave a comment below, thanks!

Posted by

Topic: Blogging
Tags: , , , , , ,

If you enjoyed this post, sign up for updates (it's free)

Feedback, questions and comments are also welcome on my Facebook page