A simple technique for web app maintenance modes using Apache

Sometimes we need to take our web apps offline temporarily (usually for scheduled major updates or emergency repair work), whereby regular users are shown a simple static page notifying them that the site is unavailable but still giving developers/admins full access to the site to test changes. Of course you could implement this in your application, but wouldn't it be nice if you could get your web server to do it all for you?

Its not as hard as it sounds thanks to some great Apache modules, most notably mod_rewrite.

Here are the basic rules we regularly use at Neutron Creations (I'll go in to more detail in a moment), these go inside our .htaccess file, but with some modification could go in the main httpd.conf file if you'd prefer.

This technique uses the presence of a file (APP_ROOT/maintenance.txt) to enforce a set of rules. To enter maintenance mode simply create this file with touch APP_ROOT/maintenance.txt, to leave maintenance mode remove this file with rm APP_ROOT/maintenance.txt. You'll need to replace APP_ROOT with whatever makes sense in your application environment.

I tried to set this relative to Apache's DOCUMENT_ROOT but could not get this to work, if anyone knows how to do this please do let me know in the comments. Thanks to ianjs for pointing out the correct syntax for this, you can use %{DOCUMENT_ROOT} in place of an absolute value for APP_ROOT to reference your document root

Part I

This provides developers (or anyone who knows the secret URL) with continued access to the app by setting a cookie that will be remembered for the session, and used in later rules. You will need to change YOUR-DOMAIN.COM to be the correct domain for your app, you could also add an expire time on this cookie if you wish (more info in the mod_rewrite flags docs).

You will also need to ensure the SUPER_SECRET_URL is set to something suitable, complex enough to be unguessable by users but easily read out if case you need to give it out over the phone.

Part II

This is the key set of rules for what we're trying to achieve, and reads like this: IF we are in maintenance mode AND the request is not for the maintenance page AND the AllowMaintenance cookie is not set THEN redirect the request to the maintenance page.

The maintenance page must be completely self contained within this maintenance directory, as any sub-requests outside of this path (for any CSS/JS assets used on the maintenance page for example) will be redirected to the maintenance page themselves.

With this directive, Apache will serve an internal redirect so the user will still see their original URL in their address bar, allowing them to refresh to try again later. If you want an external redirect so that the user sees the '/maintenance' URL instead, then just add the R flag to this RewriteRule.

Part III

This part could be quite important depending on how aggressively you cache HTTP requests in your application. Using an environment variable that's only set if you're in maintenance mode, we override any cache related headers sent from your app, making all requests expire immediately and telling proxies/caches to avoid caching anything going forward. You'll need mod_headers enabled for this part to work.

So there you have it, a very simple method to gracefully shift your web app into maintenance mode, completely independent of your application code itself. I'm sure this could be expanded upon and I'd love to see any ideas you might have for building on this technique, so drop a comment here and let me know.

Update: Its important to make sure that if you are NOT using an external redirect in Part II that you need to be careful linking to any assets as any relative paths will be relative to the url that user was trying to reach. Try to use absolute links where possible. I wasn't clear about this before, thanks to Dasha for pointing this out.

Update 2: I've been asked if it would be possible to serve a 503 (or similar) response to search engines to prevent them indexing the maintenance page. I have an alternate solution for this, if you serve an external redirect with a status code '307 - Temporary Redirect', by adding the R=307 flag in Part II, this will tell search engines to ignore the response and continue to index the original page. In addition you can add a 'Retry-After' header to inform them when to try again, just add this after the other Header directives Header set Retry-After 1800 env=MAINTENANCE which says to try again after 30 minutes.

Marc Roberts is Principal & Co-Founder at Neutron Creations, where he rules over all web development and technical direction. His favourite drink is a sweet manhattan and his favourite sandwich is a croque-monsieur. For a plethora of miscellany follow Marc Roberts on twitter.