"Requested URL not found" when using Apache with mod_alias and mod_rewrite

Problem: My web server is Apache. When I try to load a web page, I get an error message that says “The requested URL /some/file/on/the/server was not found on this server”. However, /some/file/on/the/server does exist in the server’s filesystem. What is going on?

Cause: One of the possibilities is that you are using both mod_rewrite and mod_alias and something is mis-configured. Some people have recommended against using both modules together and I agree. At least in Apache 2.0+, order of execution among the modules (i.e. which module runs before which) is determined by some priorities hard-coded into the modules and cannot be easily determined by the users. This makes your system’s behavior uncertain. (For mod_alias and mod_rewrite, the official documentation of Apache 2.2’s mod_rewrite seems to imply that mod_alias is executed before mod_rewrite. I’m not sure if this also applies to other versions, machines or modules.)

The Alias directive from mod_alias converts URLs into file-paths. E.g.

Alias /image/ /home/www/image/
basically says that if the URL starts with /image, look for the file under the path /home/www/image/. Something interesting (and confusing) happens when it is used together with the per-directory (i.e. defined in .htaccess) RewriteRule from mod_rewrite.

Let say Alias is executed first and RewriteRule second. If the RewriteRule matches the URL, “the rewritten request has to be re-injected into the Apache kernel, as if it were a new request” (per the doc for mod_rewrite). Then the new request’s URL is actually the rewritten (by RewriteRule) form of the file path given by the Alias directive. This is probably not want you want.

Confusing? Here is an example. Let say the server’s domain name is yourname.com. The directory /var/wwwtest has three files (one.html, two.html and three.html). The main config file httpd.conf contains:

DocumentRoot /var/www/Alias /test/ /var/wwwtest/

and /var/wwwtest/.htaccess contains:

RewriteEngine OnRewriteRule two.html three.html

For the request at http://www.yourname.com/test/one.html, we expect Apache to read /var/wwwtest/one.html. The actual process:

Request:  /test/one.htmlInternal Processing:  /test/one.html -> /var/wwwtest/one.html (per-server Alias: match)Result:  /var/wwwtest/one.html

That works as expected. Now, for the request at http://www.yourname.com/test/two.html, we expect Apache to read /var/wwwtest/three.html. The actual process:

Request:  /test/two.htmlInternal Processing:  /test/one.html        -> /var/wwwtest/one.html   (per-server Alias: match)  /var/wwwtest/two.html -> /var/wwwtest/three.html (per-dir RewriteRule: match)  [processing restarted]  /var/wwwtest/three.html -> /var/wwwtest/three.html (per-dir Alias: NOT match)Result:  /var/wwwtest/three.html

That does not work as expected and we are greeted by a 404 error. Apache thinks that the URL-path is /var/wwwtest/three.html and looks for the file under DocumentRoot, i.e. /var/www/var/wwwtest/three.html. Suppose there is no “var” sub-directory under /var/www, then you will see a line in error.log that says “/var/www/var” does not exists.

Solution: If you have to use the Alias-RewriteRule combination described above, use the RewriteBase directive in mod_rewrite together with RewriteBase. In our example, since three.html should be under the URL base /test conceptually, we add the following to /var/wwwtest/.htaccess:

RewriteBase /test

As before, for the request at http://www.yourname.com/test/two.html, we expect Apache to read /var/wwwtest/three.html. The actual process:

Request:  /test/two.htmlInternal Processing:  /test/one.html        -> /var/wwwtest/one.html   (per-server Alias: match)  /var/wwwtest/two.html -> /var/wwwtest/three.html (per-dir RewriteRule: match)  /var/wwwtest/two.html -> /test/three.html        (per-dir RewriteBase)  [processing restarted]  /test/wwwtest/three.html -> /var/wwwtest/three.html (per-dir Alias: match)Result:  /var/wwwtest/three.html

Now Apache reads and displays /var/wwwtest/three.html as expected.