A maintenance page is an important part of a well rounded deployment strategy that is sometimes overlooked.This article describes a schema that relies only on Apache and can be used with any server side language or framework, even raw html if necessary.

Who needs a maintenance page?

Any website that requires a release of major code updates or database upgrades needs to consider a maintenance page strategy. For sites with large scale hosting schemas across multiple servers including load balancing and a distributed server farm, there's a better way. They can take one server group offline and push changes behind the scenes, then switch the updated server group back and update the rest when the first group is complete. This is a safe methodology and users should never see a page out of place.

You just never know when a user - or a bot - may be visiting

For smaller sites on single server hosting this is not possible. Code must be pushed to the server while requests continue coming into the site. In this case, a maintenance page can be a good temporary solution to bad user experiences.

I first encountered the need for a maintenance page when developing using the Symfony framework. Pushes to production became problematic when requests continued to hit the site during code pushes, which triggered cache generation and out of sync assets. The push cycle took only two minutes and was scheduled off hours, but you just never know when a user - or a bot - may be visiting. Not only was this a terrible user experience for any unsuspecting visitors, it sometimes caused push failures that needed yet more pushes to fix.

Some frameworks have a built in maintenance experience. Drupal, for example, provides a maintenance mode that delivers messaging to users and limits site usability when needed. But Drupal's maintenance mode has dependencies - mainly, the site code and database need to be (for the most part) stable and functional. This may not always be the case, especially when significant code changes are in the middle of being put in place.

Code and framework requirements

You will need to have file write permission to a directory at run time.
You will use the addition and removal of a page to switch maintenance on and off.

Apache requirements

You will need to have permission to the web site root .htaccess file and apache modules installed for the following

The 503.html page

The 503 page is the maintenance page displayed to users.

Here is my maintenance page.

  • Important: Once you add the 503.html path to the ErrorDocument directive, this page cannot be removed or Apache will fail... All requests to the domain will produce an error.
  • The 503 error response consists of the contents of this page, so any request to any path made under this schema responds with its contents.
  • Since the response is coming dynamically from Apache, the URL of the request will not change, but the page content in the response body will.
  • You don't want anything dynamic in this page, and don't include any external files like css, javascript or even images if it can be avoided. You can use base64 images, and keep your JavaScript and CSS inline. Do everything so the page is self-contained. The thought behind this is, you can move any assets and directories at all behind the scenes, and this page won't be interrupted.
  • Your 503 page can have a meta refresh tag so the page will auto refresh every few minutes, and the site will come out of maintenance mode automatically for users that keep the page in their browser.
    <meta http-equiv="refresh" content="300">

The maintenance.html page

This page is the trigger that lets Apache know to respond in maintenance mode. Technically, this file only needs to exist to trigger the 503 response. You should have a script that copies the 503.html page to the maintenance.html page to keep it simple. Once your maintenance work is complete, your script should remove the maintenance.html page. Traffic will immediately begin to flow instead of being rewritten by Apache, and normal operations will return. You could add and remove this page manually if needed.

The Apache directives

ErrorDocument 503 /sites/default/files/503.html

<filesMatch "^\/sites\/default\/files\/(maintenance|503)\.html$">
  FileETag None
  <ifModule mod_headers.c>
     Header unset ETag
     Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
     Header set Pragma "no-cache"
     Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
  </ifModule>
</filesMatch>

<IfModule mod_rewrite.c>
	RewriteEngine on

	RewriteCond %{DOCUMENT_ROOT}/sites/default/files/maintenance.html -f
	RewriteCond %{REQUEST_URI} !/sites/default/files/maintenance.html$ [NC]
	RewriteCond %{REQUEST_URI} !/sites/default/files/503.html$ [NC]
	RewriteRule .* - [R=503,L]
	
	RewriteCond %{DOCUMENT_ROOT}/sites/default/files/maintenance.html !-f
	RewriteCond %{REQUEST_URI} ^/sites/default/files/maintenance.html$ [NC]
	RewriteRule .? / [R=302,L]

	RewriteCond %{DOCUMENT_ROOT}/sites/default/files/maintenance.html !-f
	RewriteCond %{REQUEST_URI} ^/sites/default/files/503.html$ [NC]
	RewriteRule .* - [R=404,L]

</IfModule>

The thinking behind the Apache setup

  • The maintenance page is served from /sites/default/files/503.html
  • The maintenance page "trigger" is served from /sites/default/files/maintenance.html
  • We don't want to show either page unless we're in maintenance mode
  • All traffic should be routed to the 503.html page while in this mode

Breakdown of the Apache directives

  • ErrorDocument defines the response of a 503 error
  • The filesMatch block makes sure the maintenance page is never cached
  • The first RewriteCond/RewriteRule block on line 16
    • Checks if the maintenance.html page exists
    • Also makes sure the actual request isn't to the maintenance.html page. This can cause recursion if it is.
    • If the maintenance.html page exists, and other conditions are met, throw a 503 error
    • A 503 error displays the 503.html page
  • The second RewriteCond/RewriteRule block on line 21
    • If the maintenance.html page does not exist
    • And the request is to the maintenance.html page
    • Redirect to the home page
    • We don't want a 404, because we want the meta-refresh to automatically send the user to the home page
  • The third RewriteCond/RewriteRule block on line 25
    • If the maintenance.html page does not exist
    • And the request is to the 503.html page
    • Return a 404
    • We don't want to show the maintenance page if we're not in maintenance mode

A note on .htaccess and Drupal

Drupal ships the .htaccess file with Drupal Core. When major version updates occur, this file can be changed. If you add these additions to the .htaccess file in your Drupal root, they will be overwritten in this case.

You can either maintain these changes manually, or a better approach would be to add them to the Apache configuration file for your web hosting.

Wrapping it up

To turn maintenance mode on, copy 503.html to maintenance.html.

To turn maintenance mode off, delete maintenance.html.

For an even better user experience, you can add a timestamp to the 503.html page.
This can be scripted as part of your deployment process, giving users assurance that the condition is only temporary.

A maintenance page can keep your deployment process insulated from web traffic while you update code and your database.

And it definitely provides a better user experience than page errors or missing content.

Last Updated November 18th, 2019