Drupal offers built-in support for sites in virtual subdirectories, e.g. www.example.com/subdir, but getting this up and running under Lighttpd is not well documented. Fear not, for it can be done with only minor configuration file tweaks and a few extra mod_rewrite directives.

Let’s get started

Before we get too much further, make sure you have a working understanding of how to configure Lighttpd to play nice with Drupal, multisite or standalone. Read my Drupal multisite on Lighttpd HOWTO for a good primer.

You’ll need to focus on two main issues to get a Drupal subdirectory install working under Lighttpd. First, you will need to configure the subdirectory’s alias to point to the shared Drupal core. Second, you will need to amend your mod_rewrite directives so that the Clean URLs work correctly.

Drupal sites directory

As I said earlier, Drupal does offer built-in support for sites within virtual subdirectories within their multisite configuration. Just as you could create a series of directories by domain underneath the sites directory (e.g. sites/www.example.com/) you can create the same with subdirectories:

  • www.example.com/subdir1 » sites/www.example.com.subdir1/
  • www.example.com/subdir2 » sites/www.example.com.subdir2/

Notice that the forward slash from the URL is translated into a “.” in the sites directory name. That’s the trick. Drupal will parse the directory names right-to-left, starting with the subdirectory, then onward to the domain.

Configure the subdirectory’s alias in Lighttpd

The alias needs to point to the same physical path as the shared Drupal core. This is crucial, otherwise Lighttpd will attempt to serve the path from the document root of the parent domain, e.g. www.example.com.You do not want this to happen. In the likely scenario of the parent domain also running Drupal, a request for a node within the subdirectory site would be translated to www.example.com/index.php?q=subdir/node/47 which will trigger a File Not Found error on the parent site. Bad. Useless. Fail.

Here’s how to configure the virtual subdirectory within Lighttpd. Locate the parent domain’s configuration block, which might look something like this:

$HTTP["host"] =~ "(www\.example\.com)" {
  # set the document-root to point to your Drupal multisite base installation
  server.document-root = "/var/www/drupal"

  # deny access to drupal files & raw CVS and php code
  url.access-deny = (
    "~", ".inc", ".engine", ".install", ".module", ".info", ".sh", ".sql", ".theme",
    ".tpl.php", ".xtmpl", "Entries", "Repository", "Root"

  # make sure that your mod_rewrite module is enabled in Lighttpd
  url.rewrite-if-not-file = ( "^/([^\?]+)(\?(.*))?" => "/index.php?q=$1&$3" )

Add your alias directive just above the rewrite-if-not-file directive:

alias.url = ( "/subdir/" => "/var/www/drupal/" )

It really is just that easy to declare the alias, but you’ll need to continue on to the next step if you want Clean URLs to work.

Configure mod_rewrite to get Clean URLs working

You will need to add some extra rules to your mod_rewrite directive. You will need to make sure that these new rules appear above any existing rules, as they are processed in order. Your goal with these rules is to catch anything that is requested underneath www.example.com/subdir/.

Here is a correctly-functioning virtual subdirectory url.rewrite-if-not-file block:

url.rewrite-if-not-file = (
  # handle subdir installs by explicitly ignoring ($0) static locations
  "^/subdir/(sites/|files/|misc/|modules/|themes/|system/test/|update.php).*$" => "$0",

  # handle subdir installs - root path
  "^/subdir/?$" => "/subdir/index.php",

  # handle subdir installs - all others are drupal paths
  "^/subdir/([^\?]+)(\?(.*))?" => "/subdir/index.php?q=$1&$3",

  # add this part ONLY if there is a Drupal-powered parent
  # (note: this would already be in the block if that were the case,
  # so just make sure it's last in the chain)
  "^/([^\?]+)(\?(.*))?" => "/index.php?q=$1&$3"

Save your configuration file and restart Lighttpd to try out the new virtual subdirectory site. You’ll probably need to run install.php at this point, but the installation procedure will behave exactly the same as any other Drupal installation.

Why we explicitly ignore ($0) static locations

For whatever reason, Lighttpd’s url.rewrite-if-not-file sends the rewrite rule back up the chain to the parent when there is a physical file. This is not how Apache behaves in the same scenario, so this might be where a lot of people trip up.

I’m not sure if this is a bug that will be fixed in future versions, or if this is intended behavior, but in either case, the fix is quite simple. All we need to do is send a boolean ‘false’ return value to Lighttpd’s mod_rewrite when it encounters physical directories and files. Fortunately with a standard Drupal install, the physical directories are easily predicted.


So, at this point, you should now have a working virtual subdirectory Drupal site under Lighttpd. You should feel proud!