Moving the Document Root with mod_rewrite

If you host a website with BlueHost, you don’t get to choose the directory for your domain’s document root. Your primary domain (i.e., the one you signed up with) will have a document root of ~/public_html/, and any additional domains will be sub-directories of that (e.g., ~/public_html/xplus3/).

That doesn’t give you much flexibility if you want to do something crazy like keep your sites organized. I would much rather see my primary domain’s document root at ~/public_html/xplus3.net/www, so I can keep subdomains in directories that aren’t also subdirectories of the primary domain’s document root, not to mention keeping entirely different domains out of the directory tree for the primary domain.

My problem seems to be common enough that BlueHost gives you some basic instructions for using mod_rewrite to achieve essentially the same thing. Their formula is slightly lacking, though.

Let’s say you have a sub-directory of your site called “downloads” that you want to be accessible at http://xplus3.net/downloads/. Simple enough, and BlueHost’s instructions will work for that. But what if you go to http://xplus3.net/downloads (note that there’s no slash at the end).

Normally, Apache would see that downloads is a directory and automatically redirect the visitor to the URL with with slash at the end. But with mod_rewrite first sending the request to the www directory, you end up with a redirect to http://xplus3.net/www/downloads/. Not only is that a little ugly, it can also cause problems if you have an application like WordPress in the directory. WordPress tries to send you to the post for www/downloads instead of downloads, which will likely result in either a 404 or the incorrect post.

To fix this, you need an additional rewrite rule before those rules given in the above instructions.

RewriteCond %{HTTP_HOST} ^(www\.)?xplus3\.net$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\..+$
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.*)$ $1/ [L,R=301]

This will redirect the browser to the slash-terminated version of the URL (assuming that it’s not already slash-terminated and that it’s not the URL for an existing file). After that redirect, BlueHost’s standard script should be able to handle everything else cleanly.