Nginx behind Apache reverse proxy with access restrictions

Posted on 14 March 2018
3 minute read

I have a couple of sites that are sitting behind Basic HTTP Authentication restrictions. They're simple applications that don't need full-blown built-in access control and are served over HTTPS, so Basic HTTP Auth serves just fine as a restriction control.

However, I do like to be able to access these either from internal, or specific static IP addresses without the need to have to log in all of the time, so I configured Apache in such a way that this can be achieved, for example, within a <Directory...></Directory> block:

Order deny,allow
Deny from all
AuthName "Restricted"
AuthUserFile /path/to/.htpasswd
AuthType Basic
Require valid-user
Allow from 192.168.0.0/16 10.0.0.0/8 1.2.3.4
Satisfy Any

This will then prompt for Basic HTTP Auth credentials unless the client IP address is one of the listed IP addresses / ranges in the Allow from... list.

This has worked very well for years, until I decided to build a new server at home. Unfortunately, I only have the one WAN IP address, so my servers are accessed via NAT, meaning that I can't publicly expose 2 servers on the same port, so whilst migrating sites and applications from the old server to the new one, I replaced the configuration per-site in Apache to act as a reverse proxy to the new Nginx server. This isn't a complex setup, but in case you're wondering, it looks something like this within a <VirtualHost...></VirtualHost> directive:

ProxyPreserveHost On
SSLProxyEngine On
SSLProxyCheckPeerCN On
SSLProxyCheckPeerExpire On
ProxyPass / https://10.10.10.10/
ProxyPassReverse / https://10.10.10.10/

This directs all traffic for this vhost to the Nginx server on 10.10.10.10 via HTTPS.

The problem arises when the traffic hits the Nginx server, as the client IP address then assumes the IP address of the Apache server, so irrespective of whether the actual client IP was within the Allow from... list or not, Nginx would permit access because the traffic came from within an internal IP address that was within the Allow from... address due to the reverse proxy Apache configuration.

To resolve this turns out to be quite simple. The real client IP address is forwarded from the Apache proxy to Nginx in the X-Forwarded-For header. We can use this in Nginx and convert that to become the real client IP address as if we'd hit the Nginx server directly with the following configuration.

In the Nginx Server { } section for the site, add:

real_ip_header X-Forwarded-For;
set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.1.2;
real_ip_recursive on;

where 192.168.1.2 is the Apache proxy server IP address.

The rest of the Nginx config for the Basic HTTP Auth that I've put within a Server { } block, as I want the entire site / application covered by the rules looks something like:

satisfy any;

allow 192.168.0.0/16;
allow 10.0.0.0/8;
allow 1.2.3.4;

deny all;

auth_basic "Restricted";
auth_basic_user_file "/path/to/.htpasswd"

So there we have it. Apache working as a reverse proxy to Nginx with restrictions covered by Basic HTTP Authentication and whitelisted IP addresses to by-pass the authentication requirements as if we were hitting the Nginx server directly as a public facing entity.