Andrew Welch

Andrew Welch · Insights · #devops #craftcms #performance

Making the web better one site at a time, with a focus on performance, usability & SEO

· 5 min read ·

Stop using .htaccess files! No, really.

Every CMS under the sun has you configure a .htaccess 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 standard part of the webdev 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 hosting was commonplace: sysadmins needed a way to allow multiple clients to access their server under different accounts, with different configurations for their websites. The .htaccess file allowed them to modify how Apache works without having access to the entire server.

The Virtual Private Server, or VPS, has made this a thing of the past. Excellent hosting companies like Linode, Digital Ocean, Vultr, etc. give you an entire virtualized server that is completely insulated and self-contained. And yet, like an appendix, .htaccess persists.

Indeed, modern web servers such as Nginx don’t even have the concept of a .htaccess file, because times have changed.

Why does it matter, you say? In a word: performance. Don’t take my word for it, here’s what the Apache Foundation (the creators 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 highly regarded HTML5 Boilerplate 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 section on devoted to When (not) to use .htaccess files. In a nutshell, if you are using .htaccess files, every single request the webserver handles—even for the lowliest .png or .css file—causes Apache to:

  • Look for a .htaccess file in the directory of the current request
  • Then look for a .htaccess file in every directory from there up to the server root
  • Coalesce all of these .htaccess files together
  • Reconfigure the webserver using the new settings
  • Finally, deliver the file

Every. Single. Request. And every webpage can generate dozens of requests. This is overhead you don’t need, and what’s more, it’s completely unnecessary unless you are in an old-style shared hosting environment (and if you are, stop reading this article and switch to a modern VPS immediately).

The problem gets worse the more things are added to the .htaccess file. Often it’s a dumping ground for things like rewrite rules and such, and the more complex your .htaccess file is, the more overhead is added when Apache has to parse it for every request.

Tangent: A better solution for 301 redirects is to have the CMS system process the redirects only after a 404 exception has occurred, ala the Retour plugin for Craft CMS. The rationale is that it’s better to have the webserver be performant for the majority of normal requests, rather than the exceptional redirect requests. There are exceptions, of course: the http → https redirect should always stay server-side, because of how frequently 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]

Looks simple enough. It’s just doing some fancy stuff to make sure you can have pretty looking URLs without index.php in them, and to make sure that 404 errors are routed through Craft. This is pretty typical of what many CMS systems have you put into a .htaccess file.

For every domain on your webserver, there is a Virtual Host file that tells the webserver how to serve the web pages for that domain. All we need to do is transfer what we used to put in our .htaccess file into the Virtual Host file, and we’re done. It’s really that easy.

Everything from your http -> https rewrite rules to expires headers, gzip compression settings, and other performance enhancement settings 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 actually read this far, I’ll assume you’re onboard with freeing yourself from using .htaccess files. But how do we do it? Simple. We just need to take the directives that were in the .htaccess file, and put them in our Virtual Host .conf file. These files are usually located in /etc/httpd/conf.d or /etc/httpd/sites-available on most Linux distributions.

There is one other small thing we must do as well. Even if we don’t even have a .htaccess file on our server, Apache will still look for them if the AllowOverride directive tells it to. So we just set AllowOverride none in our Virtual Host file, and that tells Apache that it doesn’t need to look for .htaccess files anymore.

Here’s what a complete Virtual Host .conf file looks like for Craft CMS. Other CMS systems should look pretty similar:

<VirtualHost *:80>
  ServerAlias *
  DocumentRoot /var/www/BradsForMen/public
  CustomLog /var/log/httpd/ combined
  ErrorLog /var/log/httpd/

## Canonical domain rewrite ##
  <If "%{HTTP_HOST} != ''">
    Redirect "/" ""

  <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]

Note that we subtly 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 difference this makes. In truth, it’s not a huge gain. You can expect about a gain in the single digits, which will become a greater impact as traffic increases. However, given how completely unnecessary it is to use .htaccess files, there’s really no reason not to implement the best practice of eschewing its use.

Even seemingly small performance gains add up when implemented together.

${ category } · ${ blog.postDate }

${ blog.title }

#${ tag.title }