The best way to set your htaccess and rewrite your URLs

The best way to set your htaccess and rewrite your URLs

Last updated: 2020-08-01 By Timothé Mermet-Buffet

Pre-requisite

Your apache server must have AllowOverride set to True for the folder you will put your .htaccess. Usually, this is set on the default configuration of apache, or sometimes by specific VirtualHost.

TL;DR

Here is the code for the lazy guys, copying this is quite ok but make sure you understand it before deploying to production!

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

How to configure the URL rewriting?

Enabling mod_rewrite

<IfModule mod_rewrite.c>
  RewriteEngine On

  RewriteRule ^(.+)$ index.php
</IfModule>

Now, you've enabled the rewriting for every URL!

But, if you tried this only, you will discover that you can't access your images/css/js files… Quite annoying for serving web pages.

Let's see how to specify to not redirect when the files are available.

Restrict the rewriting only for non-existing files and directory

The URL rewriting module provides the RewriteCond instruction to specify conditions on the next RewriteRule.

Here, we want to disable the rewriting for the folders -d and the files -f that exists in our directory.

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^ index.php [L]

Now, that's cool! We get our content and we can see our web pages with style and images.

Unfortunatly, you will realise that the visitors can see the content of the subfolders you have. It's a problem if you have content you don't want to make available for everyone.

Let's see how to disable the default listing of folders.

Disabling the listing of your folders

This time, we are going to use another apache module mod_negotiation (see Content negotiation) to set the options on our folder view.

The Options allow us to set the way we want our folders to be listed. In our case we want to disable the MultiViews and the Indexes.

According to the apache documentation for MultiViews:

The effect of MultiViews is as follows: if the server receives a request for /some/dir/foo, if /some/dir has MultiViews enabled, and /some/dir/foo does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name. It then chooses the best match to the client's requirements.

About the Indexes option:

The index of a directory can come from one of two sources:

A file located in that directory, typically called index.html.

Otherwise, a listing generated by the server.

Automatic index generation is enabled with using Options +Indexes.

In order to disable theses options (or make sure they are disabled), we have to specify them in the Options directive prefixed by the -. On the contrary, if we wanted to add theses options we will put a +.

<IfModule mod_negotiation.c>
    Options -MultiViews -Indexes
</IfModule>

Troubleshooting

My index consider my route /api different from /api/

In order to solve this problem, we will redirect all the route with a trailing slash if they are not a folder. In our case, /api/ will be redirected to /api.

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]

NOTE: This should be done before the rewriting on the index!

My Authorization header is not passed to my script

If you're using the Authorization header, you may notice that this one is not passed to your PHP script.

Usually, this is due to some apache module that strip this header, for example the basic_auth module.

In order to solve this issue, we are going to copy our Authorization header to the environment variable (E=) HTTP_AUTHORIZATION.

RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

That's it!

About the author

Timothé Mermet-Buffet

I'm a software engineer passionate about computer sciences. I love to learn and experiment with new innovations in electronics, machine learning, and distributed systems. Oh, and by the way I'm also the CEO of AlienGen, a software agency with people as crazy as me!

Want help on a similar project ?

Thanks for getting in touch!

Oops! Something went wrong.