If for some reason you need the following things out of an NGinX server:

  • Multiple virtual blocks
  • Basic authentication (with IP whitelist of approved IPs
  • Customized error messages

there are some things you should probably be aware of, as the virtual block configuration is not obvious.

First off, custom error messages are straightforward if and only if you do not have basic auth enabled.

If you do, you need to ensure two things. Your error_page flag needs to reside above the location block of that specific virtual host.

One thing I wasn’t aware of with NGinX virtual blocks is that the root should be (at least in this configuration) inside the location block. If you put the basic authentication at the server level, it will apply to all locations inside that configuration.

Here’s a consolidated example of how all the pieces go together.

server {
     listen 80 default_server;
     listen [::]:80 default_server ipv6only=on;

     server_name yoursitenamehere.com www.yoursitenamehere.com;
     
     root /var/www/yoursitenamehere.com/html;

     # path to your custom error page & the error you're handling
     error_page 401 =401 /401.html;

     # turn off basic auth, allow from all IPs,
     # disallow public access to that page
     location = /401.html {
    	auth_basic off;
        allow all;
        internal;
     }

     location / {
       

        # this is super important, unless you set this, 
        # you'll get the default nginx error pages across the board
        proxy_intercept_errors on;
        
        try_files $uri $uri/ =404;

        index   index.html;

        # satisfy first the whitelisted IPs
        # then fall back to basic, then fail everything else
        satisfy any;

        # Your whitelist IPs
        allow 111.222.333.444;
        allow 555.666.777.888;

        # basic auth
        auth_basic "Forbidden";
        auth_basic_user_file /etc/nginx/.htpasswd;

        # tell everyone else to leave with your fancy error messages
        deny all;
	}
}

The key in the above configuration is this one line proxy_intercept_errors on. This combined with containing the root inside the location block, will allow NGinX to return your custom error page.

Anyway, I recently burned some time with this, thought I’d share my findings, it was not easy to find a working combination of flags.