Simple .htaccess for ExpressionEngine Sites

September 14, 2010
by Jacob Russell

Looking for a basic, EE focused .htaccess file without the overhead of an add-on? We’ve got you covered.

Leevi Graham’s LG .htaccess Generator is a simple and well built add-on for handling EE .htaccess files, and if you’re looking for the easiest way to implement a file like this, it’s tough to beat.  It does, however, introduce a small amount of overhead because it runs every time a template is saved.  So if you’re looking for the fastest option a flat file still holds the edge.  This is the base of the file we use on every site we build, and it’s very similar to what LG .htaccess Generator creates with one exception: We’ve found that the exclude method for removing index.php works a little better for us than the include method because we keep our html directory clean (using an assets folder for css/js/etc.) but use a lot of templates.

Using the exclude method means you need to add a new entry to the .htaccess file every time you add a new folder not controlled by EE (/js in the html root, for example).  The benefit is that you don’t have to add anything when creating a new EE controlled folder like a new template group.  We’ve found that it makes more sense to handle our sites that way since we add more template groups than other folders.  If the include method works better for you there’s a link included to get you started.

We’re sharing our .htaccess with you to make building EE sites just a little bit easier; feel free to discuss any additions or changes you would make in the comments!

# Standard .htaccess file

# Secure .htaccess file
<Files .htaccess>
 order allow,deny
 deny from all

# Don't show any directory without an index file
Options -Indexes

# Dont list files in index pages
IndexIgnore *

# EE 404 page for missing pages
# May differ depending on where your template is located.
ErrorDocument 404 /index.php?/site/404

# Enable Rewrite Engine
RewriteEngine On
RewriteBase /

# Remove the www from the URL
# You may be able to do this through your web host or you may not need it at all.
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,L]

# Force the www (not used here but listed for reference)
# RewriteCond %{HTTP_HOST} !^www\.
# RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L] 

# Add a trailing slash to paths without an extension
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule ^(.*)$ $1/ [L,R=301]

# Remove index.php
# Uses the "exclude method"
# This method seems to work best for us, you might also use the include method.
# Exclude root files
RewriteCond $1 !^(favicon\.ico|index\.php|path\.php|php\.ini) [NC]
# Exclude EE folders
RewriteCond $1 !^(system|images|themes)/ [NC]
# Exclude 3rd party folders
RewriteCond $1 !^(css|js|assets)/ [NC]
# Remove index.php
RewriteRule ^(.*)$ /index.php/$1 [L]

# Remove IE image toolbar
<FilesMatch "\.(html|htm|php)$">
  Header set imagetoolbar "no"


Erwin Heiser 09.14.10

Erwin Heiser

Just thought I’d point out that this html5 boilerplate template also has a well-commented htaccess file, a few tricks you can pick up there as well.

Rob Sanchez 09.14.10

Rob Sanchez
  1. Add a trailing slash to paths without an extension

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$

RewriteRule ^(.*)$ $1/ [L,R=301]

In EE2 (like it or not), URLs are built without trailing slashes. I wouldn’t recommend this rule for EE2 as it has been known to break some forms. If anything, you’d want to have the reverse rule, to remove trailing slashes.

Leevi J Graham 09.14.10

Leevi J Graham

Thanks for the LG .htaccess generator mention (there’s also a EE2 version).

I used to use the exclude method and it works great if you can keep your directories clean. However as I found out some clients like to get “adventurous” with FTP and start uploading files anywhere and everywhere. Of course his can be avoided with good client education and a dedicated uploads folder added to the exclude rule.

You may also need to add the following rule to your final exclude rule just incase someone browses directly to a file:

RewriteCond %{REQUEST_FILENAME} !-f

Another thing to consider is serving up a text 404 error for assets such as images, js and css. This will cut down the page loading if an asset cannot be found.

# Simple 404 for missing files
<FilesMatch “(\.jpe?g|gif|png|bmp|css|js|flv)$”>
  ErrorDocument 404 “File Not Found”

All things considered .htaccess can be a black art depending on your server setup.

To clarify: The overhead of my particular addon is very low, just a single file write when an entry or template is updated.

Tyssen 09.15.10


If you’re using Google Analytics, you might also need:

RewriteCond %{REQUEST_URI} ^/$

RewriteCond %{QUERY_STRING} ^(gclid=.*)

RewriteRule ^(.*)$ /index.php?/ [L,PT]

Bransin 09.21.10


I sometimes ask myself what are the benefits of removing the index.php from a url? By default EE is setup this way, so why modify what is existing and works? The index.php in a url doesn’t affect SEO, so what are your reasons for removing it?

Jacob Russell 09.22.10

Jacob Russell

It’s ugly, it serves no purpose to the user, and it ruins good, readable URLs.  Also there’s no reason not to remove it. :)

Ryan Masuga 09.23.10

Ryan Masuga

Playing devil’s advocate about index.php: “Ugly” is subjective, and it’s arguable whether anyone is really paying attention.

Another alternative that is rarely talked about is renaming it – because it can be renamed to whatever you want, which avoids all the hassles of trying to remove it, and simplifies the .htaccess file.

An example of changing that to something useful was Apple, who put a phone number in their URL, but it could also be changed to a single character, which I’ve also seen.

See the EE Docs on Renaming the Main Site File for more info.

nevsie 09.23.10


Sorry, but there is 100% a SEO reason to remove it. It might be slight, but reasons exist. Keyword density in the URL is a factor in ranking, as well as keyword position. SO the shorter the URL, the nearer to the beginning the keyword is, the better. The less diluted the URL with non keyword word and phrases the better (better keyword density). Yes Google can/does ignore certain word, and index.php is probably one of them, but i would rather know i have done things correctly, than guess on Google’s methods. And as said above – it does make it prettier, and easier to re-enter for the client (usability).

Nevin Lyne 10.08.10

Nevin Lyne

I think it depends on site.  EngineHosting we renamed it to web_hosting, works out well in the URI structure ie: for example.  Sites like leave index.php in the URI, and of course many others remove it all together.  I think its a personal/project-centric choice to make, not a one size fits all thing.  As a small note in a basic .htaccess file I would always recommend to add in mod_expire lines to set expire headers (for any web site) for things like css, images, js, etc. to keep server traffic down for repeat site visitors.

Nice write up by the way :)

Steve Hurst 10.12.10

Steve Hurst

I have an ignorant question. When I’ve run into issues with Leevi’s excellent addon in the past, I simply leave:

RewriteCond %{QUERY_STRING} !^(ACT=.*)$ [NC]

RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5})$

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteCond %{REQUEST_URI} ^/(js|site|style|_embeds|_layouts|principals|services|members|P[0-9]{2,8}) [NC]

RewriteRule (.*) /index.php/$1 [L]

Without the line specifying the included folders, and it seems to work perfectly.

Can anyone more knowledgeable explain how this isn’t a good approach?

Steve Hurst 10.12.10

Steve Hurst

Oops, that code isn’t correct (or commented).

bq.RewriteCond %{QUERY_STRING} !^(ACT=.*)$ [NC]

RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5})$

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule (.*) /index.php/$1 [L]

Moonbeetle 10.13.10


If your hosting allows it, the .htaccess can also be used for overriding PHP ini values, if needed. If you’re using the more memory hungry add-ons, chances are you need to.

php_value memory_limit 32M
php_value upload_max_filesize 10M
php_value max_execution_time 200

Ryan Masuga 10.13.10

Ryan Masuga

I really have to fix code samples in comments, eh? I realize they look horrible.

Mark Croxton 10.21.10

Mark Croxton

Just thought I’d add that if you are trying to get this to work on a Windows server using IIRF then you’ll run into problems because IIRF’s PCRE regular expression engine does not support the negation ! symbol at the start of the pattern. For a solution that worked for me, see:

JeremyBechtold 05.17.11


in addition to nevsie’s comment above, another reason is for future maintenance. I’ve had this come up svereal times in recent years either in going from asp/.net to php or in reverse depending on the project/client/server/cms…

if you remove the index.php or default.asp(x) then there never is a need to 301 specific pages to another version of the same page just to change the extension. it might not be you maintaining or moving those files in the future, but being able to ignore. this might be out of the scope of a normal EE site issue, but since the comments already went down this road, I figured I’d go another half block…

You must be registered member to comment. If you're already a member, log in now.