Spend less time updating Drupal websites


I manage around 50 Drupal installations on shared hosting accounts. We have shared hosting (versus dedicated hosting) because most installations are rather small. Of course, I want to keep them up to date. But when a core security update is released, updating takes too long. You have to login on each account, take a back-up, put the site in maintenance mode, enable drupal update module, update the website, test,... Actually, I want to spend less time updating all these websites and do the things I like: site building, front-end development,...

In order to diminish the hassle, I looked for more scalable update methods.

Redirect to a common domain in settings.php

For SEO purposes, it's advisable to redirect a www to a non-www or a non-www to a www path. Otherwise there is duplicate content (like www.example.com and example.com with the same content), which gets penalized by the search engines. The default way of setting up this redirect is by uncommenting one of these lines in the .htaccess file:

If your site can be accessed both with and without the 'www.' prefix, you can use one of the following settings to redirect users to your preferred URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:

To redirect all users to access the site WITH the 'www.' prefix, (http://example.com/... will be redirected to http://www.example.com/...) uncomment the following:
RewriteCond %{HTTP_HOST} .
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

To redirect all users to access the site WITHOUT the 'www.' prefix, (http://www.example.com/... will be redirected to http://example.com/...) uncomment the following:
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]

But after every core update, the .htaccess file is overwritten.

Luckily, there is a solid alternative: redirecting by using the settings.php file, which is untouched during a Drupal core update!

For example, when you want to redirect all urls from www to non-www (which I use most):

// Remove www
if ($_SERVER['HTTP_HOST'] == 'www.example.com') {
	header('HTTP/1.0 301 Moved Permanently');
	header('Location: http://example.com'. $_SERVER['REQUEST_URI']);

Copy the snippet to the top of the settings.php file

Drupal settings.php remove www

When you want to redirect all urls from non-www to www:

// Require www
if ($_SERVER['HTTP_HOST'] == 'example.com') {
	header('HTTP/1.0 301 Moved Permanently');
	header('Location: http://www.example.com'. $_SERVER['REQUEST_URI']);

More details about redirecting with the settings.php file in general, check the excellent resources at Pantheon.

Exclude modules from updating

Some modules have been patched and are hard to update. Other modules are not in the repository, like the md_slider module (which is a paid module). Or, sometimes a newer version of a module is incompatible with another module.

The easiest way to exclude a module is adding a file inside the root of the module folder (under /sites/all/modules/modulename), a hidden file .drush-lock-update.

This file prevents the module being updated. Drush will also tell there is a lock.

Implement SSH Keys for fast access to files and folders

A decent shared hosting should allow acces by using SSH keys. When using SSH keys, you can login without typing a password and it's really secure (but make sure your computer is up-to-date and don't use hacked software). When generating a key pair, you end up with a public and private key. When both match, you can log in.

Create the RSA key pair:

ssh-keygen -t rsa

Store the keys and passphrase: 

Enter file in which to save the key (/Users/Username/.ssh/id_rsa):

Press enter

Enter passphrase (empty for no passphrase):

Press enter

Afterwards the public and private keys are visible in /Users/Username/.ssh/id_rsa and /Users/Username/.ssh/id_rsa.pub.

Finally, copy the key. My hosting provider (Openminds) has an interface for that.

I need to copy all the text into the box (so including the ssh-rsa beginning).

For more information on this topic, check the excellent tutorial at Digital Ocean.

Learn drush

Drush is the Drupal shell, perfect to perform common task. A good shared hosting service should also have Drush installed or it should be installable by the end user.

I use the archive-dump command for drush alot, shortened:

drush ard

This saves a back-up of both the file system and the database.

After that, I perform:

drush up

This updates core and contrib modules in one go.

And, most of the time I also perform:

drush cron


drush cc all

Just because I use these all the time!

Set up drush aliases

With aliases, it's possible to run drush commands on different websites at once.

You need to have Drush installed at the localhost. And the external servers need to have Drush through SSH.

The aliases file is located in the .drush hidden folder, (normally) found in the Home folder of the user. When there is no file, you can create one for all the websites, for example:


Mine looks like this (replace 'account' with the name of the account):

$aliases['local'] = array(
 'root' => '/Users/Bram/Sites/account',
 'uri' => 'account.local:8888',
 'path-aliases' => array(
    '%dump' => '/Users/Bram/Sites/account-local-dump.sql',

$aliases['staging'] = array(
 'root' => '/home/account/apps/staging',
 'remote-host' => 'shared-022.openminds.be',
 'remote-user' => 'account',
 'uri' => 'staging.account.drupalshopname.prvw.eu',
 'path-aliases' => array(
	'%dump' => '/home/account/account-staging-dump.sql',

$aliases['live'] = array(
 'root' => '/home/account/apps/default',
 'remote-host' => 'shared-022.openminds.be',
 'remote-user' => 'account',
 'uri' => 'account.be',
 'path-aliases' => array(
	'%dump' => '/home/account/account-live-dump.sql',

When using this alias file, I can check the status of all these installations:

drush @allwebsites status

Back-up all the websites at once:

drush @allwebsites up

Or update all the installations at once:

drush @allwebsites up

Of course, with power comes responsibility!

Use a shell script and crontab for core security updates

This method automates Drupal core updates.

The Drupalgeddon exploit showed us that hackers are faster than ever, when an exploit is anounced. Imagine you are on a journey or ill or imagine it's a public holiday and no one is babysitting over all your client websites! Security threats like these can be avoided by setting up a crontab that runs during the night to run a update of only Drupal core. The core is most likely to be the target of hackers and updates of core hardly go wrong... Of course, use at your own risk!

First, we have to create a shell script for updating Drupal core, lets name it 'update.sh': and a website account named 'examplesite'

# Go to the site directory
cd /home/examplesite/apps/default

# Clear all the cache
drush cc all

# Enable update manager, update the site and disable update manager
drush -y en update
drush -y up drupal --security-only
drush -y dis update

# Remove CHANGELOG.txt
rm -f CHANGELOG.txt

# Run cron
drush cron

You could change 'drush -y up drupal --security-only' by 'drush -y up drupal'. Or you could extend the script to create a back-up first. When creating back-ups, be aware your host normally doesn't have infinite storage!

Next, we need a crontab in order to run this command. I always choose to run such commands during the night.

Log in with Terminal and SSH and open crontab with the -e switch:

crontab -e

Insert text with:


Add a line like this:

0 4 * * * /home/examplesite/update.sh

This line refers to the shell script we created earlier. The script will run every night at 4.

Next, hit:


And finally type the command to save and quit


Followed by




Hi Bram,

Thanks for the great suggestions. These helped me a lot.

But the code for redirecting to 'www' in settings.php trows an error when the site is handled by drush. Therefore I use it like this:

// Check if Drupal is not running via command line & add www
if (php_sapi_name() != "cli") {
if ($_SERVER['HTTP_HOST'] == 'mydomain.be') {
header('HTTP/1.0 301 Moved Permanently');
header('Location: //www.mydomain.be'. $_SERVER['REQUEST_URI']);

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.