Andrew Welch · Insights · #devops #craftcms #performance

Published , updated · 5 min read ·


For more tools, technologies, and techniques, check out the devMode.fm podcast!

Stop using .htaccess files! No, really.

Every CMS under the sun has you con­fig­ure a .htac­cess file if you’re using Apache. Don’t do it!

997321 Code

Stop using .htaccess files! It seems like heresy. The .htaccess file has been a stan­dard part of the web­dev world for so long, it’s like an old friend. And yet, it’s time to say goodbye.

.htaccess files were born out of an era when shared host­ing was com­mon­place: sysad­mins need­ed a way to allow mul­ti­ple clients to access their serv­er under dif­fer­ent accounts, with dif­fer­ent con­fig­u­ra­tions for their web­sites. The .htaccess file allowed them to mod­i­fy how Apache works with­out hav­ing access to the entire server.

The Vir­tu­al Pri­vate Serv­er, or VPS, has made this a thing of the past. Excel­lent host­ing com­pa­nies like Lin­ode, Dig­i­tal Ocean, Vul­tr, etc. give you an entire vir­tu­al­ized serv­er that is com­plete­ly insu­lat­ed and self-con­tained. And yet, like an appen­dix, .htaccess persists.

Indeed, mod­ern web servers such as Nginx don’t even have the con­cept of a .htaccess file, because times have changed.

Why does it mat­ter, you say? In a word: per­for­mance. Don’t take my word for it, here’s what the Apache Foun­da­tion (the cre­ators of Apache) have to say:

You should avoid using .htaccess files completely if you have access to httpd main server config file. Using .htaccess files slows down your Apache http server. Any directive that you can include in a .htaccess file is better set in a Directory block, as it will have the same effect with better performance.

The high­ly regard­ed HTML5 Boil­er­plate warns against it as well:

(!) Using `.htaccess` files slows down Apache, therefore, if you have access to the main server configuration file (which is usually called `httpd.conf`), you should add this logic there.

They even have an entire sec­tion on apache.org devot­ed to When (not) to use .htac­cess files. In a nut­shell, if you are using .htaccess files, every sin­gle request the web­serv­er han­dles — even for the lowli­est .png or .css file — caus­es Apache to:

  • Look for a .htaccess file in the direc­to­ry of the cur­rent request
  • Then look for a .htaccess file in every direc­to­ry from there up to the serv­er root
  • Coa­lesce all of these .htaccess files together
  • Recon­fig­ure the web­serv­er using the new settings
  • Final­ly, deliv­er the file

Every. Sin­gle. Request. And every web­page can gen­er­ate dozens of requests. This is over­head you don’t need, and what’s more, it’s com­plete­ly unnec­es­sary unless you are in an old-style shared host­ing envi­ron­ment (and if you are, stop read­ing this arti­cle and switch to a mod­ern VPS immediately).

The prob­lem gets worse the more things are added to the .htaccess file. Often it’s a dump­ing ground for things like rewrite rules and such, and the more com­plex your .htaccess file is, the more over­head is added when Apache has to parse it for every request.

Tan­gent: A bet­ter solu­tion for 301 redi­rects is to have the CMS sys­tem process the redi­rects only after a 404 excep­tion has occurred, ala the Retour plu­g­in for Craft CMS. The ratio­nale is that it’s bet­ter to have the web­serv­er be per­for­mant for the major­i­ty of nor­mal requests, rather than the excep­tion­al redi­rect requests. There are excep­tions, of course: the http → https redi­rect should always stay serv­er-side, because of how fre­quent­ly it occurs.

Here’s the default .htaccess file that ships with Craft CMS:

<IfModule mod_rewrite.c>
	RewriteEngine On

	# Send would-be 404 requests to Craft
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
	RewriteRule (.+) index.php?p=$1 [QSA,L]
</IfModule>

Looks sim­ple enough. It’s just doing some fan­cy stuff to make sure you can have pret­ty look­ing URLs with­out index.php in them, and to make sure that 404 errors are rout­ed through Craft. This is pret­ty typ­i­cal of what many CMS sys­tems have you put into a .htaccess file.

For every domain on your web­serv­er, there is a Vir­tu­al Host file that tells the web­serv­er how to serve the web pages for that domain. All we need to do is trans­fer what we used to put in our .htaccess file into the Vir­tu­al Host file, and we’re done. It’s real­ly that easy.

Every­thing from your http -> https rewrite rules to expires head­ers, gzip com­pres­sion set­tings, and oth­er per­for­mance enhance­ment set­tings will all work too.

If it worked in .htaccess, it will work in your virtual host .conf file as well. Both are just ways to configure your webserver, but doing it once when the webserver starts up vs. every request is more performant.

Link Git ’Er Done

Since you’ve actu­al­ly read this far, I’ll assume you’re onboard with free­ing your­self from using .htaccess files. But how do we do it? Sim­ple. We just need to take the direc­tives that were in the .htaccess file, and put them in our Vir­tu­al Host .conf file. These files are usu­al­ly locat­ed in /etc/httpd/conf.d or /etc/httpd/sites-available on most Lin­ux distributions.

There is one oth­er small thing we must do as well. Even if we don’t even have a .htaccess file on our serv­er, Apache will still look for them if the AllowOverride direc­tive tells it to. So we just set AllowOverride none in our Vir­tu­al Host file, and that tells Apache that it doesn’t need to look for .htaccess files anymore.

Here’s what a com­plete Vir­tu­al Host .conf file looks like for Craft CMS. Oth­er CMS sys­tems should look pret­ty similar:

<VirtualHost *:80>
  ServerName BradsForMen.com
  ServerAlias *.BradsForMen.com
  DocumentRoot /var/www/BradsForMen/public
  CustomLog /var/log/httpd/BradsForMen.com-access.log combined
  ErrorLog /var/log/httpd/BradsForMen.com-error.log

## Canonical domain rewrite ##
  <If "%{HTTP_HOST} != 'BradsForMen.com'">
    Redirect "/" "http://BradsForMen.com/"
  </If>

  <Directory /var/www/BradsForMen>
      Options FollowSymLinks
      AllowOverride None

## Removes index.php from Craft URLs ##
      <IfModule mod_rewrite.c>
        RewriteEngine On
        # Send would-be 404 requests to Craft
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
        RewriteRule ^(.+) /index.php?p=$1 [QSA,L]
      </IfModule>
  </Directory>
</VirtualHost>

Note that we sub­tly changed the regex from RewriteRule (.+) index.php?p=$1 [QSA,L] to RewriteRule ^(.+) /index.php?p=$1 [QSA,L]

That’s it. Save it, restart Apache with sudo apachectl restart, and away you go!

You may ask how much of a dif­fer­ence this makes. In truth, it’s not a huge gain. You can expect about a gain in the sin­gle dig­its, which will become a greater impact as traf­fic increas­es. How­ev­er, giv­en how com­plete­ly unnec­es­sary it is to use .htaccess files, there’s real­ly no rea­son not to imple­ment the best prac­tice of eschew­ing its use. 

Even seem­ing­ly small per­for­mance gains add up when imple­ment­ed together.