DirectorySlash Hacking

The other day I came across the following scenario: A customer wanted to use Apache proxying to hide the virtual hostname that his customers were really pulling content from. The rewrite rule on the “masking host” (which I refer to as www.proxy.net in these examples) is easy enough:

RewriteRule ^/~(.*)$ http://user.proxiedto.net/~$1 [P]

Which works pretty well:

How the mod_rewrite [P] works.

The problem is what happens when DirectorySlash is enabled on the proxied-to host  (which it is by default). DirectorySlash fixes incorrectly identified resources – as an example, if you request a directory but without the trailing forwardslash.

GET /~gillespiem/images HTTP/1.1
Host: www.proxy.net

In this instance, you get a 301 redirect that appends a “/” to the end of the request BUT also sets the Location header to  the proxied-to virtualhostname.  The Jig is up – and now the address bar in the browser indicates the real host the end-user is speaking to :

When mod_rewrite [P] and DirectorySlash collide.

Here’s a snippet of response from the site:

HTTP/1.1 301 Moved Permanently
Date: Tue, 27 Oct 2009 16:28:27 GMT
Server: Apache/2.2.3 (CentOS)
Location: http://user.proxiedto.net/~gillespiem/images/

I’ve not been able to find an easy way to change what DirectorySlash uses in the Location header (maybe you can’t). DirectorySlash is important, so simply not using it won’t work in this application. Instead, I opted to use a RewriteMap to simply write my own version:  DirectorySlashHack and enable it in the vhost container (on the proxy-to site) ala:

DirectorySlash off
RewriteMap directoryslashhack        prg:/etc/httpd/maps/directoryslashhack
RewriteRule ^/~([^/]+)(/?.*)         ${directoryslashhack:%1*$1*$2}

While the solution is hack-ish (and the script and rewriterule could use a small bit of cleanup), it seems to work so far. The perl script determines if the requested resource is a directory, and if so it issues the appropriate 301 redirect using a customizable location header (which allows me to force the cleaned-up request back to the proxy).