Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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.?

share|improve this question
add comment

4 Answers 4

up vote 3 down vote accepted

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.

share|improve this answer
1  
I accept this answer because no solution appeared for a long time. So I simply have to live with the fact that it is not possible without setting RewriteBase or using absolute URLs in the rewrite targets. –  kayahr Sep 13 '11 at 11:36
add comment

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:

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$
RewriteRule ^(.*)$ %2index.php [QSA,L]

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 the REQUEST_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

    ([^#]*) - anything up to the separator, captures the virtual part
    #       - the separator
    (.*?)   - anything in the request uri up to what we've captured in group one,
              grabs the current directory url-path
    \1$     - group one again, ie the virtual part of the request uri
    

    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 with RewriteRule ^(.*)$ %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.

share|improve this answer
    
Thank you so much for your solution, I couldn't upvote it enough. Seems to be working on my setup. –  christian studer Mar 15 '13 at 11:22
    
@christianstuder Thanks! You are welcome :) –  hashchange Jun 13 '13 at 7:05
    
Great answer! I loved the trick! –  nrofis Mar 12 at 20:22
add comment

How about something like

RewriteEngine on
RewriteRule ^(.*)\.html $1/html.php

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?

share|improve this answer
    
This doesn't work. When the .htaccess file is located in the directory which is publicly accessible via the path /foo/bar and I access the file /foo/bar/test.html then $1 only contains /test.html. The path which is outside of the scope of the .htaccess file is stripped away by mod_rewrite. –  kayahr Jun 9 '11 at 11:48
    
@kayahr first of all, these rules don't need to be in a .htaccess file. Secondly, the .htaccess file does not need to live in the directory $path/foo/bar ... it can live in $path/. Each directory path is checked by Apache for .htaccess –  Philip Reynolds Jun 9 '11 at 12:49
1  
@Phil: I know that the rules don't need to be in .htaccess but I WANT them in .htaccess and I WANT it to be inside the directory of the web and not somewhere in a parent dir. The reason is "easy deployment". Simply unzip the web somewhere and it works out of the box. When I have to manually configure a RewriteBase or manually create some .htaccess file elsewhere or manually edit the global apache configuration then this is counter-productive. –  kayahr Jun 9 '11 at 13:03
1  
@kayahr, have you seen the way Wordpress isntall? They automatically setup .htaccess but it is generated depending on the Wordpress location from DocumentRoot. This is the only way to achieve this. add a install script that generates .htaccess, write it if the script have write permission, or else show it to use so they can manually add it to .htaccess. –  Hameedullah Khan Jun 9 '11 at 15:20
    
@kayahr Think outside the box, there are other ways to deploy your application but they involve work. Welcome to the wonderful world of devops! :) –  Philip Reynolds Jun 9 '11 at 19:31
add comment

I find Apache's documentation to be misleading:

When using the rewrite engine in .htaccess files the per-directory prefix (which always is the same for a specific directory) is automatically removed for the RewriteRule pattern matching and automatically added after any relative (not starting with a slash or protocol name) substitution encounters the end of a rule set. See the RewriteBase directive for more information regarding what prefix will be added back to relative substutions.

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.