64

I am trying to learn some .htaccess tricks. I came across the RewriteBase directive but could not get it to work properly.

I wonder what this directive does specifically and how to use it. There have been some discussions about RewriteBase in StackOverflow and the Apache documentation but still, I could not get a clear answer to my question.

Could someone show me a simple but effective example where RewriteBase can be used?

MrWhite
  • 43,179
  • 8
  • 60
  • 84
Chong Lip Phang
  • 8,755
  • 5
  • 65
  • 100
  • 2
    The same question was previous asked here: http://stackoverflow.com/questions/704102/how-does-rewritebase-work-in-htaccess (although the answer below is probably the most comprehensive) – Simon East Jan 12 '15 at 23:36
  • Possible duplicate of [How does RewriteBase work in .htaccess](https://stackoverflow.com/questions/704102/how-does-rewritebase-work-in-htaccess) – fkoessler Sep 08 '17 at 08:02

3 Answers3

119

In an htaccess file, mod_rewrite works similar to a <Directory> or <Location> container. and the RewriteBase is used to provide a relative path base.

For example, say you have this folder structure:

root
 |-- subdir1
 |-- subdir2
       |-- subsubdir

So you can access:

  • http://example.com/ (root)
  • http://example.com/subdir1 (subdir1)
  • http://example.com/subdir2 (subdir2)
  • http://example.com/subdir2/subsubdir (subsubdir)

The URI that gets sent through a RewriteRule is relative to the directory. So if you have:

RewriteRule ^(.*)$ - 

in the root, and the request is /a/b/c/d, then the captured URI ($1) is a/b/c/d. But if the rule is in subdir2 and the request is /subdir2/e/f/g then the captured URI is e/f/g. And if the rule is in the subsubdir, and the request is /subdir2/subsubdir/x/y/z, then the captured URI is x/y/z. The directory that the rule is in has that part stripped off of the URI. The rewrite base has no affect on this, this is simply how per-directory works.

What the rewrite base does do, is provide a URL-path base (not a file-path base) for any relative paths in the rule's target. So say you have this rule:

RewriteRule ^foo$ bar.php [L]

The bar.php is a relative path, as opposed to:

RewriteRule ^foo$ /bar.php [L]

where the /bar.php is an absolute path. The absolute path will always be the "root" (in the directory structure above). That means if the rule is in the "root", "subdir1", "subsubdir", etc. The /bar.php path always maps to http://example.com/bar.php.

But the other rule, with the relative path, it's based on the directory that the rule is in. So if

RewriteRule ^foo$ bar.php [L]

is in the "root" and you go to http://example.com/foo, you get served http://example.com/bar.php. But if that rule is in the "subdir1" directory, and you go to http://example.com/subdir1/foo, you get served http://example.com/subdir1/bar.php. etc. This sometimes works and sometimes doesn't, as the documentation says, it's supposed to be required for relative paths, but most of the time it seems to work. Except when you are redirecting (using the R flag, or implicitly because you have http://host in your rule's target). That means this rule:

RewriteRule ^foo$ bar.php [L,R]

if it's in the "subdir2" directory, and you go to http://example.com/subdir2/foo, mod_rewrite will mistake the relative path as a file-path instead of a URL-path and because of the R flag, you'll end up getting redirected to something like: http://example.com/var/www/localhost/htdocs/subdir1. Which is obviously not what you want.

This is where RewriteBase comes in. The directive tells mod_rewrite what to append to the beginning of every relative path. So if I have:

RewriteBase /blah/
RewriteRule ^foo$ bar.php [L]

and that rule is in "subsubdir", going to http://example.com/subdir2/subsubdir/foo will actually serve me http://example.com/blah/bar.php. The "bar.php" is added to the end of the base. In practice, this example is usually not what you want, because you can't have multiple bases in the same directory container or htaccess file.

In most cases, it's used like this:

RewriteBase /subdir1/
RewriteRule ^foo$ bar.php [L]

where those rules would be in the "subdir1" directory and

RewriteBase /subdir2/subsubdir/
RewriteRule ^foo$ bar.php [L]

would be in the "subsubdir" directory.

This partly allows you to make your rules portable, so you can drop them in any directory and only need to change the base instead of a bunch of rules. For example if you had:

RewriteEngine On
RewriteRule ^foo$ /subdir1/bar.php [L]
RewriteRule ^blah1$ /subdir1/blah.php?id=1 [L]
RewriteRule ^blah2$ /subdir1/blah2.php [L]
...

such that going to http://example.com/subdir1/foo will serve http://example.com/subdir1/bar.php etc. And say you decided to move all of those files and rules to the "subsubdir" directory. Instead of changing every instance of /subdir1/ to /subdir2/subsubdir/, you could have just had a base:

RewriteEngine On
RewriteBase /subdir1/
RewriteRule ^foo$ bar.php [L]
RewriteRule ^blah1$ blah.php?id=1 [L]
RewriteRule ^blah2$ blah2.php [L]
...

And then when you needed to move those files and the rules to another directory, just change the base:

RewriteBase /subdir2/subsubdir/

and that's it.

Jon Lin
  • 142,182
  • 29
  • 220
  • 220
  • Thank you for your comprehensive reply. Now it seems to me that RewriteBase is only useful when you want to use the R flag. When I tried this in /subdir1/: RewriteEngine on|RewriteBase /subdir1/subsubdir/| RewriteRule foo bar.php, a request for 'example.com/subdir1/foo' won't be mapped to 'example.com/subdir1/subsubdir/bar.php'. Why won't it? – Chong Lip Phang Jan 25 '14 at 08:25
  • @ChongLipPhang Not sure, but [that works for me](http://i.stack.imgur.com/LJOGL.png). – Jon Lin Jan 25 '14 at 08:59
  • 1
    @JonLin, So is there a `RewriteBase` equivalent for the "capturing URLs"? For example, inside `/subdir1/.htaccess` I have the command `RewriteRule ^(.*)$ -` which captures the string `foo` when I visit `/subdir1/foo`. How can we make it capture the entire `/subdir1/foo` instead of merely `foo`? – Pacerier Dec 10 '14 at 08:02
  • 2
    the %{REQUEST_URI} contains the entire path – Jon Lin Dec 10 '14 at 09:58
  • 6
    @JonLin Great job! Thank you for the detailed reply, and for not just sending us to the original docs. Original docs are obviously important, but frequently lack a clear tutorial for those new to the topic. I now have a much better understanding of mod_rewrite rules and can approach original docs without fear and trembling! :) – Chris Simeone Jan 29 '15 at 16:08
  • 1
    @JonLin OMG Thank you for having explained once for all the usefulness and usage of `RewriteBase` how it should be explained. WTF, I read Apache's mod_rewrite `RewriteBase` documentation, I guess, three times, and didn't understand anything. Sometimes they tend to be so abstruse, don't you think that? Anyway, thanks again, this should have more credit and should definitely go into the docs – tonix May 05 '16 at 21:12
  • Seems like there's no rewriterule flag to make rewriterule in `subdir1/.htaccess` file able to see `subdir1` – Pacerier Oct 03 '17 at 06:08
  • @tonix I agree with you Apache Docs are a hard read and badly explained. – Undefined-Variables Aug 25 '20 at 22:54
  • thank you a lot, your explaination is the best and the completed one because it covered three parts of htaccess technics with clear exampls ! – sohaieb azaiez Dec 12 '20 at 08:43
5

The 1 line answer is: Rewritebase sets the basepath of rewriterule arg2.

That's literally it. (so it works like html basehref, &pointint; except it's useful since often in the context of URL-rewriting the default base [.] is not wanted.)

Rewritebase makes maintenance easier if you have multiple rewriterules. Indeed, if you're only using one line of rewriterule, then rewritebase is pretty much useless (as you can simply rewriterule the based url).


Sidenote

Of course, you could ignore rewritebase completely, by using only absolute paths for rewriterules.


Related
Pacerier
  • 86,231
  • 106
  • 366
  • 634
3

RewriteBase allows you to adjust the path that mod_rewrite automatically prefixes to the result of a RewriteRule. A rewrite within the context of .htaccess is done relative to the directory containing that .htaccess file. The immediate result of the RewriteRule is still relative to the directory containing the .htaccess file, but mod_rewrite then adjusts this result, making it relative to the document root instead. RewriteBase lets you to modify this last step, allowing you to provide an arbitrary base path which is prepended to the immediate result of the RewriteRule, instead of the document root.

For me, the key point understanding the RewriteBase directive, was when I realised that mod_rewrite applies RewriteBase to the results of the RewriteRules, not to their arguments.

The normal processing of a RewriteRule within the context of an .htaccess file is like this:

  1. Generate a requested path relative to the directory containing the .htaccess file. If for example the URI is https://www.example.com/one/two/three and the .htaccess file has the path /doc_root/one/.htaccess, then the generated path will be /two/three.
  2. The RewriteRules then rewrite the relative path from above.
  3. Prefix the resulting rewritten path above with the full file-system path to the .htaccess file
  4. Remove the path to the document root from the start of the rewritten path above.

This process results in a rewritten path relative to the document root.

When there is a RewriteBase directive, steps 1,2 and 3 remain the same, only step 4 changes. Instead of the document root, the file-system path to the directory containing the .htaccess file, is removed. Then the RewriteBase is prepended.

Example:

  • Request - https://example.com/one/two/three/file.txt
  • Document root - /var/doc_root
  • Location of htaccess - /var/doc_root/one/.htaccess
  • RewriteBase - /var/other_location/

Without the RewriteBase in the htaccess file, this is processed as:

  1. Get portion of requested path relative to htaccess - /two/three/file.txt
  2. Do the rewrite - /four/rewritten.txt
  3. Prepend the path to the .htaccess file - /var/do_root/one/four/rewritten.txt
  4. Strip off the document root - one/four/rewritten.txt

With the RewriteBase in the .htaccess file, this is processed as:

  1. Get portion of requested path relative to htaccess - /two/three/file.txt
  2. Do the rewrite - /four/rewritten.txt
  3. Prepend the RewriteBase - /var/other_location/four/rewritten.txt
Dom
  • 2,980
  • 2
  • 28
  • 41
  • Where did `/four/rewritten.txt` come in? In your example neither `/four`, nor `rewritten.txt` were part of the request, or any rule, nor mentioned anywhere in the discussion. The clarity of your explanation suddenly got muddy, and complicated. Also, do we need soooo many subdirectories and alternate files (and subdirectories) to make the point? I very much liked the concept of "use the rewriteBase *after* the rewriteCond and rewriteRule or ReDirect" - if that indeed works always. Then a random subdirectory/file group was added, like a wrench thrown in, breaking my "suspension of disbelief". – SherylHohman Jul 08 '19 at 15:00
  • @SherylHohman I believe `/four/rewritten.txt` came from "arbitrary rewrite rules" that "existed" in step 2, the part where they say "Do the write". As in, you have `/two/three/file.txt`, "do the rewrite" (e.g. apply your rewrite rules), and this happens to result in `/four/rewritten.txt`. Hope that clears it up. – Lester Peabody May 08 '21 at 04:06
  • BTW @Dom thank you very much for this excellent answer. This really helped demystify a level of the stack I've been working with for years, but never really had to "work" with it much until lately. Now I get it. – Lester Peabody May 08 '21 at 04:33