A more secure Drupal [multisite] install
I love the Drupal CMS. One of my favorite features of Drupal is the ability to do a multisite install. Several sites can share one codebase. Updates are easily rolled out to every site simultaneously. Overall, it’s a wonderful idea. But I have some problems with the implementation…
The standard way to set up a multisite install is to point each of the domain names at the Drupal install folder and let Drupal sort out which domain each request is coming from. It does a good job, too. But this method introduces some complications. For example, any content uploaded to site a is accessible from site b. A user that visits
http://site1.com/myimage.jpg will find the same image as she finds at
http://site2.com/myimage.jpg. Websites can’t have domain specific
robots.txt files either, which might hurt search engine optimization of individual sites.
An interesting side effect of this is if you want to install something in a subdirectory of your site, for example a WordPress blog at
http://site1.com/blog, that exact same WordPress blog will exist in its full glory at
Another, and perhaps more grave, problem is that all that stands between the interweb and your very own personal settings is an
.htaccess file. Install scripts, includes, site configurations and database passwords are in web accessible directories, and that is never a good thing.
We’ll look at one solution to these problems.
We’ll start by creating a couple of directories. The exact location will depend on your hosting situation. Since we want to restrict access to Drupal, it’s best if these directories aren’t accessible from the internet. In my case, this means I’ll use my home directory. Anything you type will be relative to this base directory.
mkdir drupal mkdir site1 mkdir site2
You should prob’ly replace “site1” and “site2” with something a little more creative. Like the domain name of the sites or something…
You’ll now have three folders in your base directory:
Unzip the latest version of Drupal to the
drupal directory. Normally, each of the domain names using this Drupal install would point to this directory. We don’t want that, however. This folder will not be accessible from the interweb.
We will point site1.com at the
site1 folder, and site2.com at the
site2. How exactly to do this depends on your server and your webhost. For example, my webhost has a page in their control panel where I can select the destination for each domain name…
Now we will set up drupal for multisite themes and modules.
mkdir drupal/sites/all/modules mkdir drupal/sites/all/themes mkdir drupal/sites/site1.com mkdir drupal/sites/site1.com/files mkdir drupal/sites/site1.com/modules mkdir drupal/sites/site1.com/themes mkdir drupal/sites/site2.com mkdir drupal/sites/site2.com/files mkdir drupal/sites/site2.com/modules mkdir drupal/sites/site2.com/themes mkdir site1/sites mkdir site1/sites/site1.com mkdir site2/sites mkdir site2/sites/site2.com
Make sure the files directories are writable (Drupal will remind you about this later if you don’t do it now):
chmod ug+w drupal/sites/site1.com/files chmod ug+w drupal/sites/site2.com/files
So far, we’ve got a standard drupal multisite installation. The difference here is that nobody can access the
drupal directory from the internet.
Our directory structure should now look something like this:
Copy some important files to the site directories.
cp drupal/.htaccess site1/ cp drupal/robots.txt site1/ cp drupal/.htaccess site2/ cp drupal/robots.txt site2/
settings.php file to each of the sites directories. If you don’t do this, your sites will all use the “default”
settings.php, and you’ll be all sorts of confused.
cp drupal/sites/default/settings.php drupal/sites/site1.com/ cp drupal/sites/default/settings.php drupal/sites/site2.com/
A little magic
There are a few things in the Drupal install folder that site visitors will need to have access to. Some images and CSS files, mostly. Here’s where the symlinks come in. These are special files that act like an alias to other another directory. This allows us to save disk space and upkeep time with a central codebase. But each site thinks it has it’s own copy of the code.
We’ll create symlinks to the shared resources we want to expose on the web.
ln -s /path/to/drupal/misc/ site1/misc ln -s /path/to/drupal/modules/ site1/modules ln -s /path/to/drupal/themes/ site1/themes ln -s /path/to/drupal/sites/all/ site1/sites/all ln -s /path/to/drupal/misc/ site2/misc ln -s /path/to/drupal/modules/ site2/modules ln -s /path/to/drupal/themes/ site2/themes ln -s /path/to/drupal/sites/all/ site2/sites/all
Add symlinks to the files directory, so that uploaded content will be accessible as well.
ln -s /path/to/drupal/sites/site1.com/files/ site1/sites/site1.com/files ln -s /path/to/drupal/sites/site2.com/files/ site2/sites/site2.com/files
If you plan to install site specific themes or modules they need to be linked as well.
ln -s /path/to/drupal/sites/site1.com/modules/ site1/sites/site1.com/modules ln -s /path/to/drupal/sites/site1.com/themes/ site1/sites/site1.com/themes ln -s /path/to/drupal/sites/site2.com/modules/ site2/sites/site2.com/modules ln -s /path/to/drupal/sites/site2.com/themes/ site2/sites/site2.com/themes
This step is crucial. If your site doesn’t work, or your themes don’t show up, or anything else is goofy, this is where your problem is. Visit the directories containing symlinks and type
ls -al. If the symlinks show up red and/or blinking, you have a broken symlink. Try again, or redo them with full paths (something like
Again, if something breaks, recheck your symlinks.
Now we need to be able to access Drupal’s PHP files from the internet (files like
update.php). Since we want to change the working directory before executing Drupal’s PHP files, a simple symlink isn’t quite enough. Instead, we will use a PHP file that changes to the Drupal directory then includes the file we care about.
Create a new file in the
site1 directory called
index.php. Edit the contents of this file:
<?php chdir('/path/to/drupal/'); include('./index.php'); ?>
Now create a couple more almost exactly like those ones.
<?php chdir('/path/to/drupal/'); include('./cron.php'); ?>
<?php chdir('/path/to/drupal/'); include('./update.php'); ?>
<?php chdir('/path/to/drupal/'); include('./xmlrpc.php'); ?>
install.php (delete this one after the first run! you won’t need it anymore…):
<?php chdir('/path/to/drupal/'); include('./install.php'); ?>
Copy all of these wrapper files to the
site2 directory as well.
cp site1/*.php site2/
The final result
You now have the basis for a more secure Drupal multisite install. Now files uploaded to one domain will only be accessible to that domain. Configuration and install files won’t be accessible to the bad guys so you can sleep better at night. Third party software installation is a cinch, since a subdirectory in the
site1 folder won’t show up on site2.com.
This technique is not just for multisite installations. It can be used for a more secure single site Drupal install as well… Just ignore everything about site2 :-).
A few last touches
Set up the
site1.com domain to point at the
site1 folder, and
site2.com to point at
site2. If you can’t figure out how to do this with your web host company, drop me a line or leave a comment and I’ll try to help you out.
When you configure your sites remember to let Drupal know that your file system paths are
When you add shared themes and modules, put them in
themes. To install site-specific themes and modules, add them to
Now go build your sites! Visit each site in a browser, and the magical Drupal setup process will begin. Isaac Bowman has a pretty good guide for setting up Drupal in about 10 minutes.