Welcome to WebmasterWorld Guest from 34.204.191.31

Forum Moderators: Ocean10000 & phranque

Message Too Old, No Replies

Problem with RewriteOptions Inherit

Mod_Rewrite under Apache 2.4 very different than older versions

     
4:58 pm on Sep 23, 2015 (gmt 0)

Senior Member from US 

WebmasterWorld Senior Member 10+ Year Member

joined:Nov 11, 2007
posts: 774
votes: 3


Let me preface this by saying that I admittedly do NOT administer Apache web servers. I typically only "use" Mod_Rewrite once a year or so, but have never tried to configure Apache and Mod_Rewrite before other than to use the default settings in XAMPP so that I could write some basic PHP web pages. So I have only a VERY cursory knowledge of how to configure Apache. But I have an issue that I am finding difficult to resolve. That being said...

I am trying to configure Apache and Mod_Rewrite under a recent version of XAMPP so that I can write and test redirect/rewrite rules on my local Windows machine. However, I've noticed that Apache 2.4.10 works very differently than older versions and/or other configurations that I have used before.

Previously, when I had the following setup:

/.htaccess
/category/.htaccess
/category/subcat/.htaccess

and I requested /category/subcat/some-file.ext, Mod_Rewrite would essentially concatenate the rules from all .htaccess files that existed in the various folders in the requested URL path and evaluate them in reverse order li:

/category/subcat/.htaccess
/category/.htaccess
/.htaccess

More specifically, older versions seemed to evaluate the rules as follows:

  • Evaluate the RewriteRule directives in /category/subcat/.htaccess first. It would match patterns in the RewriteRule directives against some-file.ext (i.e. it removed the portion of the path down to and including the /category/subcat/ directory where the .htaccess file being evaluated exists). If a match is found and the [L] flag is included in the RewriteRule, it would break out and do whatever the matched rule says (e.g. if [R=301,L] then it would break out of rule processing and 301 redirect to the target URL.)
  • If no match was found in /category/subcat/.htaccess it would evaluate the RewriteRule directives from the /category/.htaccess next. It would match patterns in those RewriteRule directives against subcat/some-file.ext (i.e. it removed the portion of the path down to and including the /category/ directory where the .htaccess file being evaluated exists). If a match is found and the [L] flag is included in the RewriteRule, it would break out and do whatever the matched rule says (e.g. if [R=301,L] then it would break out of rule processing and 301 redirect to the target URL.
  • If no match was found in /category/.htaccess it would evaluate the RewriteRule directives from the /.htaccess next. It would match patterns in those RewriteRule directives against category/subcat/some-file.ext (i.e. it removed the portion of the path down to and including the root directory where the .htaccess file being evaluated exists). If a match is found and the [L] flag is included in the RewriteRule, it would break out and do whatever the matched rule says (e.g. if [R=301,L] then it would break out of rule processing and 301 redirect to the target URL.


So if I wanted to redirect /category/subcat/some-file.ext I had choices.

  • I could put a rule in /.htaccess like RewriteRule ^category/subcat/some-file\.ext$ http://example.com/other-file.ext [R=301,L,NC] or
  • I could put a rule in /category/.htaccess like RewriteRule ^subcat/some-file\.ext$ http://example.com/other-file.ext [R=301,L,NC] or
  • I could put a rule in /category/subcat/.htaccess like RewriteRule ^some-file\.ext$ http://example.com/other-file.ext [R=301,L,NC]


Initially, what I was seeing under XAMPP and Apache 2.4.10 when I requested localhost/category/subcat/some-file.ext was that it ONLY executed rules in the /category/subcat/.htaccess. If no RewriteRule directives were matched to some-file.ext then rule processing stopped. It would never evaluate rules in the /category/.htaccess or /.htaccess files.

I discovered that this was because by default, under Apache 2.4 Mod_Rewrite does not "inherit" rules from parent folders. So I added a RewriteOptions Inherit directive to all of my .htaccess files.

The addition of RewriteOptions Inherit to all .htaccess files did trigger the inheriting of rules from additional .htaccess files in parent, grandparent, etc. folders, respectively, should all of the RewriteRules in the .htaccess file where the requested page "lives" fail. However, the behavior still seems to be different than in previous versions.

By this I mean that the patterns in all RewriteRules (regardless of whether the RewriteRule was in the .htaccess file where the requested page "lives" or in an inherited .htaccess file from an ancestor folder) seem to always be compared to "some-file.ext" rather than the rules from /category/subcat/.htaccess being matched against some-file.ext and rules from /category/.htaccess being matched against subcat/some-file.ext and rules from /.htaccess being matched against category/subcat/some-file.ext.

So I have a couple of questions:

Is there a way such that when RewriteOptions Inherit in being used and I request localhost/category/subcat/some-file.ext to force the RewriteRule directives in /category/subcat/.htaccess to match against some-file.ext and if no matches then for RewriteRule directives in /category/.htaccess to match against subcat/some-file.ext and if no matches then for RewriteRule directives in /.htaccess to match against category/subcat/some-file.ext?

And also, is there a place in the httpd.conf where I can place RewriteOptions Inherit once and have it apply across the enter server or to a given VirtualHost?

Thanks in advance!
6:50 pm on Sept 23, 2015 (gmt 0)

Senior Member from US 

WebmasterWorld Senior Member lucy24 is a WebmasterWorld Top Contributor of All Time 5+ Year Member Top Contributors Of The Month

joined:Apr 9, 2011
posts:15869
votes: 869


Preliminary disclaimer: Most people hereabouts are still on 2.2, so anything learned from 2.4 is welcome. The more details, the better.

by default, under Apache 2.4 Mod_Rewrite does not "inherit" rules from parent folders

Now, wait a minute. Non-inheritance has always been the rule for mod_rewrite. And, as you've noticed, even "RewriteOptions inherit" doesn't mean the mod is inherited in the way of other Apache mods, where everything is cumulative. Instead, it means "If no rule in the present htaccess or <Directory> section applies to the request, then-and-only-then go back and apply the rules from the previous htaccess or <Directory>."

is there a place in the httpd.conf where I can place RewriteOptions Inherit once and have it apply across the enter server

See below.

In per-directory context this means that conditions and rules of the parent directory's .htaccess configuration

Thank you, Apache, for that confusing and potentially misleading choice of wording. (That was from 2.2. The 2.4 version says "parent directory's .htaccess configuration or <Directory> sections" but it isn't clear whether this is a change in structure or just a change in documentation.)

Wait, here's the new part:
InheritBefore
InheritDown
InheritDownBefore
IgnoreInherit
InheritBefore is new to 2.4 (technically 2.3 and later). The others apply only in 2.4.8 and later. What exact version are you on?

If you are using Apache 2.4.8 or later, RewriteOptions InheritDown (or InheritDownBefore) will do what you need. It's a once-and-for-all directive that stays in effect unless explicitly disabled by a later "IgnoreInherit".
11:28 pm on Sept 23, 2015 (gmt 0)

Full Member

Top Contributors Of The Month

joined:Apr 11, 2015
posts: 328
votes: 24


ZydoSEO: By this I mean that the patterns in all RewriteRules (regardless of whether the RewriteRule was in the .htaccess file where the requested page "lives" or in an inherited .htaccess file from an ancestor folder) seem to always be compared to "some-file.ext" rather than the rules from /category/subcat/.htaccess being matched against some-file.ext and rules from /category/.htaccess being matched against subcat/some-file.ext and rules from /.htaccess being matched against category/subcat/some-file.ext.


Curious, I've just done some tests on both Apache 2.2.9 and 2.4.7 and I see the same results on both - which appears to be the same as you are seeing on Apache 2.4.10. To be honest I did not appreciate that the "Inherit" option worked quite like this!? I had "assumed" it worked the way you thought it did on "older versions". This is not nearly as useful as I first thought IMO - it means that the directives in the parent .htaccess have to be specifically written knowing that they will be inherited from a child .htaccess - and cannot work independently!?

If you remove the /category/subcat/.htaccess file then the directives in /category/.htaccess and /.htaccess will no longer match! Since the pattern will need to be changed to match against "subcat/some-file.ext", as opposed to "some-file.ext". Anyone care to explain the logic behind this?

However, this is as per the documentation for Apache 2.4 - "Inherit" [httpd.apache.org]:

The inherited rules are virtually copied to the section where this directive is being used.


This is not explicitly stated in earlier versions as far as I can see, however, the behaviour appears to be the same as far as I can tell.

I don't see that any of the inherit options really changes this behaviour? Does it?

The "InheritDown" option states that "It is equivalent to specifying RewriteOptions Inherit in all child configurations" - which is the same problem. (?)

My test files... (which simply sets environment variables that are output in "some-file.php"):


# /category/subcat/.htaccess
RewriteEngine On
RewriteOptions Inherit
RewriteRule ^(some-file\.php)$ - [E=ENV_MATCH_SUBCAT:$1]



# /category/.htaccess
RewriteEngine On
RewriteOptions Inherit
RewriteRule ^(some-file\.php)$ - [E=ENV_MATCH_CATEGORY:$1]
RewriteRule ^(subcat/some-file\.php)$ - [E=ENV_MATCH_CATEGORY:$1]



# /.htaccess
RewriteEngine On
RewriteRule ^(some-file\.php)$ - [E=ENV_MATCH_ROOT:$1]
RewriteRule ^(subcat/some-file\.php)$ - [E=ENV_MATCH_ROOT:$1]
RewriteRule ^(category/subcat/some-file\.php)$ - [E=ENV_MATCH_ROOT:$1]


When requesting "/category/subcat/some-file.php", all 3 environment variables are set to "some-file.php" (in both Apache 2.2.9 and 2.4.7).

Hhhmm, discuss please...
12:27 am on Sept 24, 2015 (gmt 0)

Senior Member from US 

WebmasterWorld Senior Member lucy24 is a WebmasterWorld Top Contributor of All Time 5+ Year Member Top Contributors Of The Month

joined:Apr 9, 2011
posts:15869
votes: 869


Oops, my bad, you did specify 2.4.10. So you do have the once-and-for-all "Down" option. But the part you'll need to experiment with is Inherit vs. InheritBefore. (Can't do it myself, darn it, not being on 2.4.anything yet.)

Is the opening anchor ^ essential? That is, do you risk conflict with other files that happen to have the same name, but are located in various other directories? I'm thinking of a vaguely analogous issue I had with RewriteRules inside a <FilesMatch> envelope. (You are not actually supposed to do this, but I didn't know that, and it was so convenient, I've kept it ;))
1:08 am on Sept 24, 2015 (gmt 0)

Senior Member from US 

WebmasterWorld Senior Member 10+ Year Member

joined:Nov 11, 2007
posts: 774
votes: 3


@Lucy24 Thanks. Placing a RewriteOptions InheritDown in the root .htaccess (/.htaccess) does trigger inheritance in the .htaccess files in all folders below the root like /category/.htaccess and /category/subcat/.htaccess. However I still haven't figured out where to put that statement in the httpd.conf so that I don't need it in any .htaccess file.

The docs for RewriteOptions say, "Context: server config, virtual host, directory, .htaccess" which I "thought" meant that it could appear in the httpd.conf at the server level, in a <VirtualHost /> element in the httpd.conf, in a <Directory /> element in the httpd.conf, or a .htaccess file, respectively. But I've tried it in several places in the httpd.conf at the server level, inside the <Directory /> element that denies access to the entire server filesystem as well inside the <Directory "C:/xampp/htdocs"> element which is my web root to no avail. I could only get it to work in the context of a .htaccess file.

@whitespace What you are seeing is EXACTLY my delima. Perhaps I'm thinking of how ISAPI Rewrite (a port of Mod_Rewrite that runs on Windows/IIS) inheritance works automatically. And for a page request for /category/subcat/some-file.ext...

at the /category/subcat/.htaccess under ISAPI Rewrite, RewriteRule patterns would be compared to "some-file.ext"...
at the /category/.htaccess level, RewriteRule patterns would be compared to "subcat/some-file.ext"..., and
at the root /.htaccess level, RewriteRule patterns would be compared to "category/subcat/some-file.ext". Perhaps it was implemented slightly different.

Anyway, at least I have the RewriteOptions InheritDown working in the root /.htaccess. I can accomplish what I want by using %{REQUEST_URI}.
5:49 am on Sept 24, 2015 (gmt 0)

Senior Member from US 

WebmasterWorld Senior Member lucy24 is a WebmasterWorld Top Contributor of All Time 5+ Year Member Top Contributors Of The Month

joined:Apr 9, 2011
posts:15869
votes: 869


as well inside the <Directory "C:/xampp/htdocs">

Oh, wait, this is a pseudo-server on your own computer? Drat.This is what happens when two people post questions within 24 hours of each other. I lose track of which is which. It's possible that this is actually not an Apache problem at all, but something involving permissions on your physical computer. I've had analogous issues with vhosts on my MAMP setup thanks to, ahem, being too cheap to spring for MAMP Pro which I think does it all for you. It kicks and screams and demands administrator passwords every time I do some tiny little thing that doesn't seem as if it would have anything whatsoever to do with the operating system.

Memo to self: Check whether MAMP comes in a 2.4 version so I can pursue further experimentation, even if it's safer to keep my primary setup as close as possible to what my host's real server uses.

Anyway I'm glad the question came up, because it prompted me to consult Apache docs. I was just looking for exact wording; it wouldn't have occurred to me that they'd introduce a major switch like InheritDown smack in the middle of a version number. (2.4.10?! I hadn't even realized they were past 2.4 :() But then, I'd somehow missed InheritBefore too, and that's all of 2.4.