Rails Maintenance Pages Done Right

A maintenance page is a common feature in a rails deployment. This feature enables the developers to put their application into “maintenance mode”, returning 503 (Service Temporarily Unavailable) and a helpful page that describes what is happening and when the user can expect service to resume.

In an nginx configuration file this would be something like this:

if (-f $document_root/system/maintenance.html) {
  return 503;
}

error_page 404 /404.html;
error_page 500 502 504 /500.html;
error_page 503 @503;
location @503 {
  rewrite ^(.*)$ /system/maintenance.html break;
}

The page can then be activated or deactivated from a simple Capistrano task:

# Swap in the maintenance page
namespace :web do
  task :disable, :roles => :web do
    on_rollback { run "rm #{shared_path}/system/maintenance.html" }

    run "if [[ !(-f #{shared_path}/system/maintenance.html) ]] ; then ln -s #{shared_path}/system/maintenance.html.not_active #{shared_path}/system/maintenance.html ; else echo 'maintenance page already up'; fi"
  end

  task :enable, :roles => :web do
    run "rm #{shared_path}/system/maintenance.html"
  end
end

This pretty typical configuration is lacking two desirable features. First, what if you want your logo image on your maintenance page? Lets try this:

if (-f $document_root/system/maintenance.html) {
  return 503;
}

error_page 404 /404.html;
error_page 500 502 504 /500.html;
error_page 503 @503;
location @503 {
  # Serve static assets if found.
  if (-f $request_filename) {
    break;
  }

  rewrite ^(.*)$ /system/maintenance.html break;
}

This configuration will serve any static assets nginx can find on disk, but it will continue to return the 503 HTTP Status Code.

Finally, nginx, like most webservers will not let you POST to a static file. If a user loads your login page, then you switch on your maintenance page, the user will be greeted with the standard nginx 405 (Method Not Allowed) page. The solution is to capture 405 errors in your @503 location block, serving the maintenance page. In addition, you will have to enable @recursiveerrorpages@, since you are first, intentionally, throwing a 503 error, and then the user is throwing a 405 by posting to your static file:

recursive_error_pages on;

if (-f $document_root/system/maintenance.html) {
  return 503;
}

error_page 404 /404.html;
error_page 500 502 504 /500.html;
error_page 503 @503;
location @503 {

  error_page 405 = /system/maintenance.html;

  # Serve static assets if found.
  if (-f $request_filename) {
    break;
  }

  rewrite ^(.*)$ /system/maintenance.html break;
}

Now you can serve your stylish maintenance page no matter what the situation is.