HTTPS and access
Protect private locations
Restrict health, metrics, and administrative paths by network or authentication without treating obscurity as access control.
8 minute lesson
Private endpoints often expose operational data or powerful actions: metrics, debug pages, admin panels, internal status. A hard-to-guess URL is not a security boundary. Scanners enumerate paths all day, and one leaked log line reveals the “secret” URL forever. Put a real control in front instead.
Nginx gives you two that compose well: network restrictions and authentication.
Restrict by network
Use allow and deny for trusted network ranges:
location = /status {
allow 10.0.0.0/8;
allow 127.0.0.1;
deny all;
proxy_pass http://127.0.0.1:3000;
}
Rules are evaluated top to bottom and the first match wins, so the pattern is: list the trusted ranges, end with deny all. Anyone outside the ranges gets a 403 Forbidden before the request ever reaches the application.
Restrict by authentication
For access from anywhere, add HTTP basic auth over HTTPS:
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd flavio
location /metrics {
auth_basic "Private area";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:9100;
}
Unauthenticated requests get 401 Unauthorized. Basic auth sends credentials on every request, so it belongs behind TLS only — never expose it on a plain HTTP listener.
Verify both boundaries from the outside:
curl -I https://app.example.com/status # from an untrusted network
# HTTP/1.1 403 Forbidden
curl -I https://app.example.com/metrics
# HTTP/1.1 401 Unauthorized
curl -I -u flavio:the-password https://app.example.com/metrics
# HTTP/1.1 200 OK
Test the failure cases with the same care as the success case. An endpoint you believe is restricted deserves proof from a network that shouldn’t reach it.
Now the trap: the apparent client address changes behind another trusted proxy. If a load balancer or CDN sits in front of Nginx, allow and deny evaluate the proxy’s address, not the visitor’s. Depending on the ranges you wrote, that either locks everyone out or — worse — lets everyone in because the proxy’s internal address falls inside your allowed range. In that topology you need the real IP restored from a forwarding header (the realip module), configured to trust only your proxy’s addresses, before address rules mean anything.
Create a harmless private status path on your test server. Verify access from an allowed address and rejection from a disallowed one, including the proxy-address assumptions.
Lesson completed