Some of you may say “whytf umofo”, and some may say “I’ve always wanted to be able to do that!”. This hack is for the later, but I will try below to explain to the former why it is useful. If anyone knows of an easier way to do it, please let me know.
Apache httpd serves static content out of its DocumentRoot :
DocumentRoot /usr/local/apache/htdocs
<Directory /usr/local/apache/htdocs>
Order allow,deny
Allow from all
</Directory>
The contents of that location in the filesystem might look like:
/usr/local/apache/htdocs/index.php
/usr/local/apache/htdocs/images/a.png
/usr/local/apache/htdocs/images/b.png
/usr/local/apache/htdocs/images/c.png
And to a web browser, the site looks like:
/index.php
/images/a.png
/images/b.png
/images/c.png
All very vanilla. But sometimes you want to be able to store the site in two parallel and overlapping directories in the filesystem, without changing the way it is presented to the browser.
/usr/local/apache/htdocs/index.php
/usr/local/apache/htdocs/images/a.png
/home/matt/alt_htdocs/images/b.png
/home/matt/alt_htdocs/images/c.png
If you don’t have parallel or overlapping requirements, then Alias is your friend. If you do, read on.
The Hack
Do this:
DocumentRoot /usr/local/apache/htdocs
<Directory /usr/local/apache/htdocs>
Order allow,deny
Allow from all
</Directory>
RewriteEngine on
RewriteCond "/home/matt/alt_htdocs%{REQUEST_URI}" -f [OR]
RewriteCond "/home/matt/alt_htdocs%{REQUEST_URI}" -d
RewriteRule ^/?(.*)$ /home/matt/alt_htdocs/$1 [L]
<Directory /home/matt/alt_htdocs>
Order allow,deny
Allow from all
</Directory>
If you don’t want to serve directories from the alternate docroot (Options +Indexes) then you don’t need the RewriteCond
with the -d
. Remove it and the [OR]
from the previous line.
If you want to add a restriction to the alternate docroot, for example only allow the images
directory, then put that in your regex:
RewriteCond "/home/matt/alt_htdocs%{REQUEST_URI}" -f [OR]
RewriteCond "/home/matt/alt_htdocs%{REQUEST_URI}" -d
RewriteRule ^/?(images/.+)$ /home/matt/alt_htdocs/$1 [L]
If you want three or four or five alternate docroots, just copy-and-paste the three rewrite lines as necessary. And remember you may need a <Directory>
section for each location.
How it works
If you see a file or directory in the alternate docroot, then serve it. Otherwise: DocumentRoot
.
The RewriteCond
s look into the alternate docroot, and checks if a file (-f
) or directory (-d
) exists under there that matches the REQUEST_URI
, which is something like /index.php
or /images/b.png
.
If either/both of the RewriteCond
s match, then the RewriteRule
runs. If its regex matches, then we give Apache the explicit filesystem path it should serve. If no such rules match, then the DocumentRoot
comes into play.
In some situations (if you are doing further rewrites) then you may not want the [L]
, which instructs mod_rewrite
to run no further rules for this request.
Untested
Today is the first day I’ve used this hack, so YMMV. But it seems to work very well for static files.
Preliminary testing also indicates it works for .php
files (in both the normal and alternate docroot).
I doubt it would work if trying to include one .php
into another across docroots.
The Use Case
Why would anyone, or even myself, find this useful?
Websites tend to end up like my son’s food at the end of dinner: all mushed up into a claggy mess. You may be running a couple of PHP apps, a couple of your own apps, in addition to a bunch of static files. And you may care about permalinks. And sometimes all these things overlap in the filesystem.
One solution is to copy everything into the one docroot. When it comes time to upgrade a component, you have to selectively pull out the old bits and dump in the new bits, without overwriting everything else. That sucks, and is why I always hate upgrading my WordPress instance.
The other solution is to put each component in its own directory. Upgrading a component is then the graceful process of deleting a directory and replacing it with new content. Separate directories can also have differing permissions, so that they can be edited by different people or programs.
If your components don’t overlap, then vanilla rewrites or Alias
es work very well. Otherwise this hack may come in handy.
Further work
I’m sure this could be done much more easily with a custom module, in the vein of VirtualDocumentRoot. Ideally it would be nice to say (with options for specifying priority):
DocumentRoot /usr/local/apache/htdocs
AltDocumentRoot /home/matt/alt_htdocs
It would be also nice to be able to support parallel-overlapping views in the filesystem proper. A kind of “mount” that presented a view of several parallel directories.