Maps and Custom Error Pages in Nginx

During a recent web application upgrade I had to limit access to the the web servers; I wanted the administrators and myself to be able to access the site, but for everyone else to see an “Under Construction” page. My initial plan was to test if the $remote_addr was one of the allowed IPs, and then redirect those clients to a maintenance page, but I couldn’t figure out how to test more than one IP address (seriously)!

I eventually stumbled upon the nginx map module which, combined with a custom error page, ended up being an elegant, fun solution to this problem.

Elegant maps

Here is a snippet from /etc/nginx/conf.d/default.conf which shows the important bits:

server {

    location / {
        if ($denied != 0) {
            # HTTP 503: service unavailable
            return 503;

         # Send requests to Tomcat

    error_page 503 @maintenance;

    location @maintenance {
        root /tmp;
        rewrite ^(.*)$ /maintenance.html break;

map $remote_addr $denied {
    default 1; 0; 0;

By default all IP addresses are denied (ie, $denied=1), but depending on the client’s IP address, the $denied variable can be set to 0. In the root location block I essentially test if the IP address is denied and conditionally return an HTTP 503 (Service Unavailable), which is handled by a custom error_page handler with a named location block. So cool!

In retrospect

In retrospect I probably could have used a regex in the $remote_addr test, but maps are really a more flexible, efficient, and “nginx” way of accomplishing this. On that note, I’m using nginx more and more lately and, in addition to being fast as hell and having better TLS support, it’s just more fun to use than Apache. 😉

Furthermore, to deploy this I wrote an Ansible playbook which included a list of allowed IPs and reconfigured the nginx vhost by using a Jinja2 template which iterated over the IPs to create the map block above. Very cool, and very easy to reverse when the maintenance was over!