Drupal (or any CMS) and multiple
RewriteBase
entries
Abstract
There is a general frustration with the lack of flexibility in Apache handling different URL bases. I present here a method which might be old hat to some, but if so, I have never seen it documented anywhere before. It works with Drupal in this example, or any CMS possibly with a spot of modification.
There is a common scenario with Drupal, where you want one
installation to serve multiple domains. There is a system in place for this,
but Apache is the complicating factor. You want to have what looks like
multiple RewriteBase
entries, one for each domain. The problem
is that RewriteCond
does not apply to RewriteBase
.
There are long threads on this issue on the Drupal forums, but I could not
find a solution until I cooked up this one. I like it so much I will use it
on all my sites from now, with or without Drupal.
What does RewriteBase
do? It chops off the base from the
URL, the whacks it back on at the end, so to your whole
setup it looks like the base URL is shorter than it
really is. A RewriteRule
can simulate one half of that: it can
redirect pages to the same script at a different URL
depending on the domain. The other half, unfortunately, it cannot do. But, a
PHP script in fact can. That is then the key idea.
I will illustrate with a worked example showing the flexibility. We
have one site, accessed at three different domains: the primary one, which
does not support HTTPS, as a subdirectory of the
secondary one which does have HTTPS (both run from the
same server), and from localhost on my desktop. I develop the site using a
git
clone, and check any changes before pushing them to the
live site. The clone has to have exactly the same files
as the live site—the same .htaccess
, and the same configuration
files, or else the syncing gets out of hand with the multiple
developers.
Drupal has a rather funky set-up where multiple sites and databases can be used with one install of the system. My solution does also extend easily to the case where the different domains are actually serving up different sites, though I don’t use it myself.
This is how we do it. Assume that the site is installed in the
drupal
directory, and that the all the configuration is
correct (that is, it works correctly on one of the domains you want to
use).
Example 1. The relevant section of .htaccess
<…> # The story is that the site might be accessed from three different # places: domain.com, the standard address for users # secdomain.com, for when SSL is required (all logins and admin stuff) # localhost, for development clones # We start by making sure that secdomain uses only SSL: RewriteCond %{HTTP_HOST} secdomain\.com$ [NC] RewriteCond %{HTTPS} =off RewriteRule ^(.*)$ https://www.secdomain.com%{REQUEST_URI} [L] # Also neaten the domain.com domain RewriteCond %{HTTP_HOST} ^domain\.com$ [NC] RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # We use a hacked version of index.php to simulate multiple RewriteBase # directives (which Apache cannot handle). Depending on the site, we will # redirect everything to index page, and trim it as appropriate there. RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{HTTP_HOST} domain\.com$ [NC] RewriteCond %{REQUEST_URI} !=/favicon.ico RewriteRule ^(.*)$ /drupal/index-fake.php?q=%{REQUEST_URI} [L,QSA] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{HTTP_HOST} secdomain\.com$ [NC] RewriteCond %{REQUEST_URI} !=/favicon.ico RewriteRule ^(.*)$ /<subdir1>/drupal/index-fake.php?q=%{REQUEST_URI} [L,QSA] # If not match so far, then guess! In this case, it is localhost RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !=/favicon.ico #Note that subdir2 must have same last component as subdir1 above RewriteRule ^.*$ /<subdir2>/drupal/index-fake.php?q=%{REQUEST_URI} [L,QSA] <…>
Example 2. index-fake.php
<?php /** * @file * This is the redirection file — simulate the last portion of RewriteBase * by chopping off the base from the start of any URL. */ $crop = pathinfo(realpath('..'), PATHINFO_BASENAME); $_GET['q'] = preg_replace("#^(/$crop)?/drupal#", '', $_GET['q']); require_once 'index.php';
That’s it! The magic is in index-fake.php
, where
we assume that the subdirectories on the various domains all end in the same
component and chop off the URL up to there. Multiple
RewriteBase
entries are simulated, and the Drupal install’s
won’t know that the index.php
file wasn’t called
directly.
(As a final note, I connect to the database on a different port based on the hostname in the settings file, so that the same database powers the content on the live and dev versions of the site.)