There have been a number of forum and mailing list discussions about
how to run multiple Drupal sites from a common codebase, and/or how to
install Drupal in a subdirectory rather than in the document root.
Here are a couple: http://drupal.org/node/22269
http://drupal.org/node/10843

A lot of people seem really frustrated trying to get it to work. But
it can be done! My personal interest is in setting up Drupal on a
dedicated server in a manner that makes it easy for multiple vhosts to
share a common codebase, while still allowing site-by-site
customization if necessary. With that in mind, here's a blueprint for
several different ways of installing Drupal, and the mod_rewrite
.htaccess RewriteRule implications for each.

Different places that Drupal can be installed and accessed, and how
the rewrite rules need to be changed for each case:

1) Installed in root, accessed as root

The usual current installation method. Drop the files off in the
top of htdocs, and let it happen. Direct access to existing files
and directories works (minus some path info problems), but name
conflicts are a real possibility. Done as a symlink, allows access
to common code base for multiple sites, but this makes it hard for
these sites to have outside files and scripts. The .htaccess file
works out of the box. Personally, though, I find this approach
really messy and think it would be a good idea for Drupal to move
away from it.

2) Installed in subdirectory, accessed as subdirectory

This is straightforward, and might be useful if you have a single
site but want to make it easy to switch between different versions
of Drupal. Put drupal in htdocs/drupal (or make a symlink) and
then have "http://host/drupal" set as the base in
"site/host/settings.php". The drupal .htaccess file stays
unchanged: since no Alias statement is involved you do not need to
change the RewriteBase statement.
The symlink version of this is easy for common codebase, but all
Drupal links need to include string '/drupal'. Not terrible, but
makes migrating to such a system difficult if one has an existing
web-indexed or outside-linked system.

3) Installed in subdirectory, but accessed as root.

Starts to get more interesting. Put drupal in a subdirectory (or
more likely make a symlink), then add a .htaccess file in the root
directory that redirects non-existing URL's to '/drupal'. Easy to
upgrade, allows migration from existing installation.

------------------------------------
RewriteEngine on
# Redirect root page to drupal home (if desired)
# NOTE: without trailing slash, external redirect to /drupal will
# be used and it will show up in browser. With slash, hidden.
RewriteRule ^$ drupal/ [L]
# Rewrite non-file/directory URL's to be under drupal
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ drupal/$1 [L]
-------------------------------------

The problem with this approach for a shared hosting setup is that it
requires symlinks that the user probably does not have the
permissions to create. But if you have control of the whole box,
this might not be a bad approach.

4) Installed globally (no symlink), accessed as subdirectory.
or
5) Installed globally (no symlink), accessed as root.

Finally we reach the case of having multiple sites with separate
document roots running from a common Drupal codebase. These two are
very similar. The rewrite rules in the common drupal .htaccess are
the same for both. Presuming a common Drupal codebase installed in
/var/www/shared/drupal and 'Alias /shared/ /var/www/shared/':

(in common drupal/.htaccess)
------------------------------------------
RewriteEngine on
RewriteBase /shared/drupal

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
-------------------------------------------

Or if you have Drupal aliased directly ('Alias /drupal/
/var/www/drupal/'), set 'RewriteBase /drupal/'.

The rewrite rules in the root of the site will be slightly different:

(in site root .htaccess for Drupal access at root)
----------------------------------------------
RewriteEngine on
RewriteRule ^$ /shared/drupal/ [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /shared/drupal/$1 [L]
-----------------------------------------------

(in site root .htaccess for Drupal access as subdir /drupal)
-----------------------------------------------
RewriteEngine on
RewriteRule ^drupal/(.*)$ /shared/drupal/$1 [L]
-----------------------------------------------

If you want access to look like a subdirectory, you'll want to set
$base_url in 'settings.php' to be equal to whatever your locally
aliased subdirectory is (http://host/drupal in this case). Possibly
usefully, these two rulesets can be combined if necessary to allow for
legacy links if you move from one style to another.

Stray notes:

The checks for -f and -d uses full path information when checking: so
if you have a subdir node but are looking for /node/1, -d is false.
But a search for /node/index.html will match and will be served (if
that file indeed exists). But this might cause a problem when using
other programs that require path_info.

Seems like symlinks are matched by check for "-f file". For example,
if 'file.html' is a symlink to 'real.html', "-f file.html" will be
true, contrary to what the manual says about 'regular files'.

Note that you will need to have "AllowOverride FileInfo" (or greater)
turned of for some directory above where your symlink or alias exists.
Otherwise, the symlink will be followed and the root page will be
accessible, but the RewriteRule's in the .htaccess file in the symlink
will be ignored so the rewrite rules won't work. If you are getting
'page not found' errors, you might put an intentional error in your
.htaccess file just to check that it is actualy being read.

RewriteBase is really hard to understand. When per-directory
(.htaccess) rewrite rules are used, the per-directory portion of the
URL is stripped off before the rule is applied, and then put back
after the rule. When RewriteBase is used, the per-directory portion
is still stripped, but then RewriteBase is put back afterwards.
When does one need this? Whenever one is is running out of an
Alias'ed directory, where the file path no longer matches the URL
path. It's not needed when in a subdirectory (or symlinked
subdirectory), only when dealing with aliases. In this case, one
needs to set RewriteBase to be equal to the current subdirectory but
treating the alias as root. For example, if you have "Alias /shared/
/var/www/shared', and have Drupal installed under
'/var/www/shared/drupal', the .htaccess in that directory would need
to have 'RewriteBase /shared/drupal' to work correctly.

Moving the site-specific configuration files to live in the
virtual-hosts directory instead of in the common Drupal source base is
a separate matter. It should be easy to solve with some judicious
symlinks, though, or with a very simple patch to change where the
site-specific settings.php files are searched for.

Apologies if there are errors in this. I've tested a bit, but only a
bit. As I move beyond the initial testing phase, I'm sure I'll come up with
some modifications. Probably I've missed some edge cases, or worse giant gaps.
If you see any, please add appropriate comments.

Hope this helps,

Nathan Kurz
nate@verse.com

Comments

gregoryo’s picture

In the process of upgrading from a root level 4.6 to a 4.7 based sites, I wanted to move the drupal code to a subdirectory.
This is on a shared host at pair.com.

I tried your item 3 above and found that to get clean URL's to work I needed to add index.php to your suggestion.

RewriteEngine on
RewriteRule ^$ subdir_drupal/ [L]
# Rewrite non-file/directory URL's to be under drupal
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ subdir_drupal/index.php?q$1 [L]

Only later, when my browser cache had expired, did I see that I was no longer getting the theme stylesheets and graphics.

This required using some of your notes from 3, 4 and 5.
In the top level .htaccess, I use

 RewriteEngine on
 RewriteRule ^$ /subdir_drupal/ [L]
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule ^(.*)$ /subdir_drupal/$1 [L]

and add a .htaccess file to the subdir_drupal directory as follows:

 RewriteEngine on
 RewriteBase /subdir_drupal
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

Perhaps there is a more simple appraoch, but this does work for me at the moment. I may have to revisit this as I move the document root of some sites into subdirectories.

As an additional note, I also have other sites and applications (perl based) on this server. While I've seen some .htaccess recommendations to explicitly exclude such directories from the matching rules, at the moment, I add a .htaccess files to each of these directories

Options +Indexes

This allows one to browse the other directories, but does send 404's back to the drupal system.

Chaos_Zeus’s picture

Hi,
Thank you very much for this helpful article. I am trying to simplify my future upgrading needs as well as making my life easier maintenance-wise by following this information. I have to admit that it is a steep learning curve. But, here is my setup and my question:

I have a .htaccess file configured as gregoryo describes above. However, what I am is a main page - www.chaoselbows.com/ - is equal to www.chaoselbows.com/d51/. d51 is the drupal directory where everything drupal lives. If I click on the link (a standard home "nodes" page) the uri is actually in the form of http://www.chaoselbows.com/d51/node/1603. taking out the /d51 part also works, but the default includes it. how do I get rid of this extra subdirectory showing here?

RewriteRule ^$ /d51/ [L]

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ /d51/$1 [L]

and in the subdirectory,

  RewriteBase /d51/
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

are the "relevant" portions of the .htaccess files. actually, the one in the /d51 subdir is the "default" folder with rewritebase changed.

can you offer any thoughts as to why or how to get rid of the uri showing the subdirectory?

Thank you!

steveoliver’s picture

(1) I have drupal common codebase at /var/www/share/drupal/drupal-4.6.6/
(2) I have drupal common .htaccess as such:

#
# Apache/PHP/Drupal settings:
#

# Protect files and directories from prying eyes.
<Files ~ "(\.(inc|module|pl|sh|sql|theme|engine|xtmpl)|Entries|Repositories|Root|scripts|updates)$">
  Order deny,allow
  Deny from all
</Files>

# Set some options.
Options -Indexes
Options +FollowSymLinks

# Customized error messages.
ErrorDocument 404 /index.php

# Set the default handler.
DirectoryIndex index.php

# Override PHP settings. More exist in sites/default/settings.php, but
# the following cannot be changed at runtime. The first IfModule is
# for Apache 1.3, the second for Apache 2.
<IfModule mod_php4.c>
  php_value magic_quotes_gpc                0
  php_value register_globals                0
  php_value session.auto_start              0
</IfModule>

<IfModule sapi_apache2.c>
  php_value magic_quotes_gpc                0
  php_value register_globals                0
  php_value session.auto_start              0
</IfModule>

# Reduce the time dynamically generated pages are cache-able.
<IfModule mod_expires.c>
  ExpiresByType text/html A1
</IfModule>

# Various rewrite rules.
<IfModule mod_rewrite.c>
  RewriteEngine on

  # Modify the RewriteBase if you are using Drupal in a subdirectory and
  # the rewrite rules are not working properly.
  #RewriteBase /drupal
  RewriteBase /
  # **** I have tried RewriteBase as default and as it is set (/) above with the same results -- no clean URLs ****

  # Rewrite old-style URLs of the form 'node.php?id=x'.
  #RewriteCond %{REQUEST_FILENAME} !-f
  #RewriteCond %{REQUEST_FILENAME} !-d
  #RewriteCond %{QUERY_STRING} ^id=([^&]+)$
  #RewriteRule node.php index.php?q=node/view/%1 [L]

  # Rewrite old-style URLs of the form 'module.php?mod=x'.
  #RewriteCond %{REQUEST_FILENAME} !-f
  #RewriteCond %{REQUEST_FILENAME} !-d
  #RewriteCond %{QUERY_STRING} ^mod=([^&]+)$
  #RewriteRule module.php index.php?q=%1 [L]

  # Rewrite current-style URLs of the form 'index.php?q=x'.
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
</IfModule>

# $Id: .htaccess,v 1.66 2005/03/20 19:15:00 dries Exp $

(3) I have vhost.conf file for adobeinspections.com as such:

        ServerName   adobeinspections.com:80
        SuexecUserGroup         adobeinspections psacln
        ServerAdmin  "email AT adobeinspections.com"
        DocumentRoot /var/www/share/drupal/drupal-4.6.6/
        CustomLog  /var/www/vhosts/adobeinspections.com/statistics/logs/access_log plesklog
        ErrorLog   /var/www/vhosts/adobeinspections.com/statistics/logs/error_log
        ScriptAlias  /cgi-bin/ /var/www/vhosts/adobeinspections.com/cgi-bin/
        <IfModule mod_ssl.c>
                SSLEngine off
        </IfModule>
        <Directory  /var/www/share/drupal/drupal-4.6.6/>
        <IfModule mod_perl.c>
        <Files ~ (\.pl$)>
                SetHandler perl-script
                PerlHandler ModPerl::Registry
                Options ExecCGI
                allow from all
                PerlSendHeader On
        </Files>
        </IfModule>
        <IfModule sapi_apache2.c>
                php_admin_flag engine on
                php_admin_value open_basedir "/var/www/share/drupal/drupal-4.6.6:/var/www/vhosts/adobeinspections.com/httpdocs:/tmp"
        </IfModule>
        <IfModule mod_php5.c>
                php_admin_flag engine on
                php_admin_value open_basedir "/var/www/share/drupal/drupal-4.6.6:/var/www/vhosts/adobeinspections.com/httpdocs:/tmp"
        </IfModule>
        <IfModule mod_python.c>
        <Files ~ (\.py$)>
                SetHandler python-program
                PythonHandler   mod_python.cgihandler
        </Files>
        </IfModule>
                Options +Includes +ExecCGI
        </Directory>

Yet, I cannot enable clean urls for the site. (It appears your host is not configured correctly for Clean URLs. Please check for ModRewrite support with your administrator.)

Any help is appreciated. Thanks in advance.

Regards,
-Steve

mcurry’s picture

I've posted a helpful tip on my web site:
http://exodusdev.com/multi-site-compatible-apache-rewrite-rules

The default .htaccess examples for rewrite rules were causing problems for me, too, so I found a solution and discuss it on the above-referenced page.

If someone wants to try this on their site and add their experience here (and perhaps write up a more detailed, easier-to-understand explanation) I'd be grateful.

Michael Curry - http://exodusdev.com