How to Use htaccess to Run Multiple Drupal 7 Websites on Your Cheapo Hosting Account

1096

You can run multiple websites from your inexpensive shared Web hosting account, thanks to .htaccess.

What is the point of .htaccess, anyway? It’s just another cruel hoax to keep us scared and humble, isn’t it. It may seem that way, and even the Apache devs consider it a form of near-magic. It relies on Perl Compatible Regular Expressions, so any rules you write for .htaccess are mostly punctuation.

Apache logo

Understanding at least the basics of .htaccess is essential for running any website on the Apache Web server. .htaccess is for per-directory configurations. You place it in the directory you want to do stuff to, and it acts on that directory and all of its subdirectories. You can use it for authentication, access control, mod_rewrite directives, managing multiple sites on a single server, and pretty much everything that is managed in your Apache server configuration files. (On Fedora, CentOS, and other Red Hattish distros the Apache configuration files are in/etc/httpd, and on Debian/Ubuntu/Mint they’re in /etc/apache2/.)

As .htaccess duplicates your main Apache configs, when do you use it? When you don’t have access to your server configuration, like on inexpensive shared Web hosting accounts. If you have access to your Apache server then you don’t need to bother with .htaccess, and in fact you shouldn’t use it because you’ll take a performance hit. Once you configure Apache to allow .htaccess it will look in every directory for it, even if you don’t have any .htaccess files. If you are using mod_rewrite and put rewrite rules in .htaccess, your rules will be re-compiled with every request. When rewrite rules are in your server configuration they are read once and cached.

You can use any content management system (CMS) you want, like WordPress or Joomla. We’re diving into Drupal 7 because it’s a special case that needs more complex configuration. It’s not well-documented, so hopefully this will help the hordes of frustrated Drupal 7 admins.

Drupal 7 and .htaccess

When you’re running multiple websites from a single Apache server it’s better to use Apache virtual hosts. But if you’re using an inexpensive Web hosting account you won’t have that option, so you’ll have to use .htaccess. In these examples I’m using a hosting account with CPanel and the Softaculous installer. Figure 2 shows what the public_html directory, which is the Web root, looks like with two Drupal 7 installations.

fig-2 filesystem

The rinkydink way to access each site is to include the subdirectory in the URLs, like mysite.com/drupal2 and myothersite.com/drupal3. Which is fine for testing and when you don’t care. But when you do care and want URLs like mysite.com and myothersite.com, you need .htaccess. If your cheapo hosting account lets you run multiple domains first set them up in CPanel under Domains. Then you use .htaccess to direct traffic to the correct sites.

Drupal 7 needs special handling, because you need to edit the .htaccess file in your Web root, and Drupal’s.htaccess and settings.php files. This is what my Web root public_html/.htaccess looks like:

Options -Indexes
Options +FollowSymLinks
RewriteEngine on  
# Serve www.mysite.com from drupal2 directory RewriteCond %{HTTP_HOST} ^www.mysite.com$ [NC] RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301] RewriteRule ^(.*+)$ drupal2/$1 [L,QSA]
# Serve www.myothersite.com from drupal3 directory RewriteCond %{HTTP_HOST} ^www.myothersite.com$ [NC] RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301] RewriteRule ^(.*+)$ drupal3/$1 [L,QSA]

You can copy and paste this for your own Drupal 7 sites, and all you have to change is the domain name in the RewriteCond rule and your site directory in the second RewriteRule for each site. Next, enter your settings.php file (mine is public_html/drupal2/sites/default/settings.php) and set your base_url. Look for a line like this, uncomment it, and type your site’s base URL:

$base_url = 'http://mysite.com';  // NO trailing slash!

“Base URL” is one of those terms that sounds like you should be doing something special. It’s just your site URL. Now you must edit your Drupal root .htaccess. In my example that is public_html/drupal2/.htaccess. This is a fat file all full of good stuff. Look for this line:

RewriteRule ^ index.php [L]

Comment it out, and add this line:

RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

And you’re done. A couple of notes:

— Drupal updates will stomp your Drupal .htaccess, so keep a backup copy, or do the rename-rename dance before and after applying updates. 
— You must protect your settings.php with restrictive permissions. Change it to read-write when you need to edit it, and then change it to 0400 (owner read only) when you’re finished.

What Just Happened

Let’s dissect this so we know what is going on.

RewriteCond %{HTTP_HOST} ^www.mysite.com$ [NC]

RewriteCond defines the conditions for rewriting a URL. The syntax is RewriteCond TestString CondPattern. The TestString is evaluated and then compared to the CondPattern. If the test string matches the conditions then the result is passed to the next rule.

The TestString is the %{HTTP_HOST} variable, and the CondPattern is ^www.mysite.com$. This means “examine the incoming HTTP request headers, and when HTTP_HOST equals www.mysite.com then apply the following RewriteRules”. ^ indicates the start of the string to be matched, and $ marks the end of it. The backslashes escape the dots, because the dot can be either a literal dot or a metacharacter. [NC] is a RewriteRule flag that means “no case”, or case-insensitive.

Want to see what HTTP request headers look like? You can see them in your Web browser. In Firefox try Tools > Web Developer > Web Console > Network (figure 3).

fig-3 http headers

RewriteRules do the heavy lifting. You can have multiple rewrite rules, and each one is applied in order. The syntax is RewriteRule Pattern Substitution [flags].

RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301]

Our first RewriteRule redirects all the pages on the site. It matches URLs starting with www.mysite.com, and any path component (REQUEST_URI) that follows it; in other words, all the other pages on the site. How, you ask, does %1 magically know it represents www.mysite.com? That is a RewriteCond backreference. Backreferences are created internally by Apache and are always present for your coding pleasure. The backreference %1 was created automatically from our RewriteCond rule. So http://%1%{REQUEST_URI}> means all the pages on the site.

[L,R=301] means stop processing when matching URLs are found (L) and do a 301 redirect (moved permanently). When you’re testing new redirect rules you should use 302 (temporary redirect) so you don’t confuse search engines and hork your SEO.

RewriteRule ^(.*+)$ drupal2/$1 [L,QSA] is the final step that makes the nice www.mysite.com URL work, instead of www.mysite.com/drupal2. We already know what the caret and dollar sign mean. The conditional expression inside the parentheses is three metacharacters, which combined create a wild card that matches everything. It also creates another backreference, which is used in the substitution drupal2/$1. L is stop processing when the conditions are met, and QSA means append. So put it all together and it makes drupal2/ the site root.

If you’re thinking this is all a little brain-bending, it surely is. The official Apache documentation is worth studying, and there are number of excellent Apache books, and no Apache admin should leave home without them.