Why do I get a double trailing slash depending on where my RewriteRule is located?

I am using the following code to direct all www requests to non-www URLs:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.org$ [NC]
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]

This works great inside an .htaccess file in the root of my website.
For example,
www.example.com -> example.com/
www.example.com/ -> example.com/
www.example.com/other_page -> example.com/other_page

However, if I move this same code into my VirtualHost configuration, the rewritten URLs contain a double trailing slash.
www.example.com -> example.com//
www.example.com/ -> example.com//
www.example.com/other_page -> example.com//other_page

I fixed it by removing the slash from the rewrite rule:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.org$ [NC]
RewriteRule ^(.*)$ http://example.com$1 [R=301,L]

But I can't understand the reason for this. Anyone know why?


Solution 1:

As I understand it, in .htaccess files, the string that mod_rewrite processes in your rule is relative to the directory the .htaccess file is in, so it will not have a / at the start.

In the VirtualHost entry, the string it processes is absolute to the root of the server, and so includes the /.

It makes for subtle differences in how mod_rewrite works.

Here is someone with a similar problem, and solution:

http://forum.modrewrite.com/viewtopic.php?p=56322&sid=77f72967f59200b5b5de174440234c3a

This should work in both cases, assuming I remember my escaping correctly:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.org$ [NC]
RewriteRule ^\/?(.*?)$ http://example.com/$1 [R=301,L]

Solution 2:

It is happening because you are capturing an initial slash with (.*) and then applying another slash before it in the new location /$1. It didn't happen before because mod_rewrite behaves slightly differently when operating in a per-directory context as opposed to a per-server context.

You could avoid this by optionally pre-empting the slash. Additionally you can use RedirectMatch in an empty VirtualHost with your surplus domains, which creates a bit less processing and can look cleaner.

<VirtualHost *>
ServerName example.com
ServerAlias other.example.com
..
RedirectMatch permanent ^/?(.*) http://example.com/$1
</VirtualHost>

Solution 3:

I'm including this post for completeness.

The Apache documentation explains why this behaviour occurs very well and is the reason that the 'RewriteBase' directive exists.

Simply including the 'RewriteBase' directive in your .htaccess file should achieve your desired result.

Example:

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.example\.org$ [NC]
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]

From the Apache 2.2 mod_rewrite documentation:

The RewriteBase directive explicitly sets the base URL for per-directory rewrites.

My rule of thumb is to almost always use 'RewriteBase' in .htaccess files, and not to use it in the Apache config.