Flexible Maintenance Mode via nginx Proxy

1 minute read

There is a common pattern I’ve seen, in guides about Ruby on Rails deployment via Capistrano, to setup a maintenance pages or a maintenance mode. The guides help you configure nginx such that the existence of the maintenance page causes the nginx proxy to return the maintenance page instead of forwarding on to the unicorn/puma/passenger server.

This approach generally looks like this:

server {
# basic stuff, listen, server_name, ssl, root...
  if (-f $document_root/system/maintenance.html) {
    return 503;
  }
  error_page 503 @maintenance;
  location @maintenance {
    rewrite  ^(.*)$  /system/maintenance.html last;
    break;
  }
# application specific stuff, location blocks...
}

The limitation here is that when you place the $document_root/system/maintenance.html file on the server, no one can get to your site, even you.

We can rarely access our applications by routing around the proxy. The proxy does too much, serving assets, unifying services, adding headers, doing important routing, etc. Even if none of that is true, often our application server is listening on a socket, not via HTTP, so even some creative port forwarding via ssh won’t expose it to us remotely.

There is another approach! Leverage nginx variables!

server {
# basic stuff, listen, server_name, ssl, root...
  if (-f $document_root/system/maintenance.html) {
    # enable maintenance mode if the file exists
    set $maint_mode 1;
  }
  if ($remote_addr = 10.0.0.1) {
    # disable maintenance mode for your IP (replace with your IP)
    set $maint_mode 0;
  }
  # Any other checks you'd like can `set $maint_mode 0;`
  if ($maint_mode) {
    # return the 503 if we hit this block and maintenance mode is still on
    return 503;
  }
  error_page 503 @maintenance;
  location @maintenance {
    rewrite  ^(.*)$  /system/maintenance.html last;
    break;
  }
# application specific stuff, location blocks...
}

You can add as many different checks around the $remote_addr section as you’d like. Check multiple IPs, check for a specific url path (maybe just enable the admin interface first), check for a special header, whatever you’d like!

Note: If your IP changes, don’t leave this block sitting around in your config.

It is pretty simple, and not a huge change, but it can really help you spot check some maintenance work before letting the world back into whatever is behind your nginx proxy.