I want to create a mod_rewrite RewriteRule which is independent from the location where the web page is installed. I want to define the rewrite rule in a .htaccess file. Let's take this as an example:
RewriteEngine on
RewriteRule ^(.*)\.html html.php
With this rule I want to map all *.html requests to a html.php script which is located in the web root. The problem is, the public base url of the webroot can change. So the web root could be located on http://www.somewhere.tld/
or in some sub directory at http://www.somewhere.tld/foo/bar/
.
But using a relative path in a rewrite rule doesn't work. So I have to write one of these:
/html.php (When web is located in root directory of the web)
/foo/bar/html.php (When web is located in foo/bar sub directory)
Alternatively I can set a RewriteBase but I simply don't want to configure this path at all. I want apache to automatically do the right thing so I can just copy the web to some directory and it just works without telling the rewrite rules where the web is located in. How can I do this.?
I have struggled with the same problem, and for the same reason. I am trying to make a web app independent of the location where it is installed, without resorting to a config script or manual user intervention. Just drop the app somewhere and let it do its thing.
And it appears there is a solution after all, at least for Apache 2. Takes four lines. Explaining the thinking behind it takes more than four lines, though ;)
Tl;dr try this:
Here is the how and why
We can't set the RewriteBase dynamically, so we set it once and for all to the root URL of the server:
RewriteBase /
. This provides consistency, but it also means that we have to establish the url-path to the current directory ourselves and prefix it to the rewritten URL.So which directory are we in? Let's assume a
REQUEST_URI
of /some/path/app-root/virtual/stuff. Our htaccess is in app-root. If we grab the virtual part - virtual/stuff - and remove it from theREQUEST_URI
, we are left with the url-path to our app directory.Capturing the virtual part is straightforward and can happen in the rewrite rule itself.
RewriteRule ^(.*)$ ...
makes it available in the$1
variable.Now we do our little string operation and remove the virtual part from the request URI. We don't have string commands for that, but RewriteCond can match strings and capture substrings. So we'll add a RewriteCond with the sole purpose of extracting the url-path to the current directory. Other than that, the "condition" should stay out of our way and always be true.
We can use the
$1
variable from the RewriteRule in the RewriteCond because mod_rewrite actually processes a ruleset backwards. It starts with the pattern in the rule itself, and if it matches, goes on to check the conditions. So the variable is available by then.While the test string in the RewriteCond can use the variable, the actual condition regex can't. Inside the condition, we can only use internal back-references. So first, we assemble a test string "[virtual part][some separator][request uri]". The '#' char makes a good separator because it won't show up in the URL. Next, we match it against a condition of
So here's the full condition:
RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$
.The second captured group in the RewriteCond regex is our location. We just need to prefix it to the rewritten URL with a
%2
reference. That leaves us withRewriteRule ^(.*)$ %2index.php [QSA,L]
. Voilà.I haven't done extensive testing yet, but I've established that it works with ordinary virtual hosts as well as mass virtual hosting (using VirtualDocumentRoot). By implication, other aliased locations should be fine as well.
Apache 1.3
Apache 1.3 is still around, unfortunately, and it will choke on the RewriteCond pattern. Apache 1.3 doesn't support the ungreedy modifier (the '?' in
(.*?)
).But for Apache 2, it should do the trick. I'd definitely appreciate any feedback, though, in particular if it fails in your environment.
Edit: I have just posted a more comprehensive article about the subject on my blog ("Using mod_rewrite in .htaccess files without knowing the RewriteBase"). See there for more details.
There is no way as in my knowledge that you can achieve this. You have to configure RewriteBase. One way is to automate setting up of RewriteBase using a PHP script maybe? But that will need write permission (at least) on the .htaccess. But you will have to configure the RewriteBase in .htaccess.
I find Apache's documentation to be misleading:
But the prefix it adds back is completely different (path on disk instead of original URL). I can't think of a situation where this would be the correct behavior.
How about something like
My example preserves the filename part of html file, though -- for example, page1.html would get redirected to page1/html.php. (Note: I did not test this at all, try at your own risk :))
Also, mod_rewrite guide has tons of examples similar to your problem. Did you take a look at that already?
It's not clear in the question what actually determines the location of the "web root" in this context? (As it clearly differs from the document root.)
For the sake of answering, I assume the "web root" is the directory in which the web application is installed. This should also be the location of the
.htaccess
file controlling this "web application" (not in the document root).In which case, you can simply use a relative path substitution. You must not use a
RewriteBase
directive (which overrides the directory-prefix that is added back later and cannot be set "dynamically").So, if the "web root" is
/foo/bar
then the.htaccess
at/foo/bar/.htaccess
should read something like:(The
RewriteRule
pattern suffix^(.*)
is superfluous here.)So, given a request for
/foo/bar/baz/something.html
then the request will be internally rewritten to<document-root>/foo/bar/html.php
(filesystem path).<document-root>/foo/bar/
is the "directory-prefix" (the filesystem path of the location of the.htaccess
file) that is added back to all relative path substitutions.Standard front-controller pattern (
.htaccess
file located at<web-application-root>/.htaccess
):Don't set the
RewriteBase
directive.Note that the question and all examples on this page are dealing with internal rewrites, not external redirects. Only if we are dealing with external redirects would we need to (dynamically) set/compute the effective base URL (or
RewriteBase
). Because if you allow the directory-prefix to be added back to a relative substitution in the case of an external redirect, then you will most probably experience an invalid redirect.