Do htaccess rules cascade into subfolders

Do htaccess Rules Cascade

Do htaccess rules cascade into subfolders within your website?  By default they do.  However they do not cascade the same way that many of us might think they do.  Let’s explore how the cascading rules apply.

In CSS, it is easy to learn the cascading rules by simply taking the farthest to closest approach.  Rules defined the farthest away from the element are applied first.  Rules defined closer to the element are applied next and will override as needed.  Using this rule the CSS styles are applied in this order:  “External Styles” -> “Page Header Styles” -> “Inline Styles”.

htaccess rules take a different approach.  Rules defined in a subfolder are applied first, then rules defined in a parent directory are applied second.  

Note:  Not all of the apache rules are able to follow this cascading approach – so for this post we will focus on our efforts on the mod_rewrite rules.

Example Scenario

Let’s explore the following example where we have a public URL of: http://www.example.com/blog/post/my-post-title

In your /blog/post folder you have a single PHP page named “index.php” which takes in a slug “my-post-title” and displays the appropriate post content from your database.  In this example you want to access the index.php file but without actually forwarding the request and changing the URL.  The htaccess rules you use might look like the following:

RewriteCond %{REQUEST_URI} !index.php
RewriteRule ^(.*)$ index.php?slug=$1&%{QUERY_STRING} [L]

Now at your root directory, you decide that you want to force your entire website to be HTTPS and not HTTP.  This means that to your users you want to change the URL http://www.example.com/index.php to be https://www.example.com/index.php.  In your root htaccess file you include the following:

RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.example.com/$1 [R,L]

This essentially takes any traffic over port 80 and sends it to your secure domain.  You notice though that it works for your entire website, EXCEPT for you blog/post folder.  The reason for this is because in our /blog/post/ folder we are already invoking a RewriteRule and cannot invoke a second from a parent htaccess file. 

From the Apache Tutorial on htaccess

The configuration directives found in a .htaccess file are applied to the directory in which the .htaccess file is found, and to all subdirectories thereof. However, it is important to also remember that there may have been .htaccess files in directories higher up. Directives are applied in the order that they are found. Therefore, a .htaccess file in a particular directory may override directives found in .htaccess files found higher up in the directory tree. And those, in turn, may have overridden directives found yet higher up, or in the main server configuration file itself.

Remember, everything is working exactly as it should in our htaccess files – it is just not accomplishing the goal we want.  We will have to change around our approach a little bit depending on what is currently going on with your website.

Option 1: Reduce / combine htaccess files

The first option might be to reduce the overall number of htaccess files that are in use in our site.  In this case we could take the directives specified in the /blog/post/.htaccess files and add them to our root .htaccess file.  In this case you will need to adjust the conditions being used to isolate them to just that one folder.  Your new .htaccess file may look like this:

RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.example.com/$1 [R,L]

RewriteCond %{REQUEST_URI} blog/post/
RewriteCond %{REQUEST_URI} !blog/post/index.php
RewriteRule ^blog/post/(.*)$ blog/post/index.php?slug=$1&%{QUERY_STRING} [L]

There are several advantages to this approach.  First is that this allows you apply the rule to force all requests to https before applying any other rewrite rules.  Second is that this approach allows all of your htaccess rules to be found in one place.  

By contrast there are a few downsides to this approach.  The main downside is that this htaccess file is loaded and evaluated for every single page in your website.  Being realistic, if you only have a few htaccess rules, you will never notice this.  However if your site manages thousands of pages and has hundreds of rules, it may be more efficient for your web server to use more granular htaccess files or add these rules to the general http.conf file.

Option 2: Add top level rules to our individual htaccess files

This second option is not as ideal for maintenance reasons.  However if your site is setup in a way where you need to define rules as needed in different htaccess files, even though they might be duplicate rules, then this is a valid approach that will work.  Simply copy and paste the ruleset that redirects to https.  Be sure to adjust for any folders as needed.

RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.example.com/blog/post/$1 [R,L]

RewriteCond %{REQUEST_URI} !index.php
RewriteRule ^(.*)$ index.php?slug=$1&%{QUERY_STRING} [L]

There are pros and cons to either solution.  the one that is best for you really depends on your website and individual needs.

Posted in , and tagged , , , , , , .