Admin panels, metrics endpoints and staging sites need protection at the edge, and headers harden every page.
NGINX has several simple access controls:
allowanddeny(access module) check the client address in order; the first match wins. End withdeny all;.auth_basic "Restricted";withauth_basic_user_fileasks for a username and password, stored in an htpasswd file (create it withhtpasswdfrom apache2-utils oropenssl passwd). Only use it over HTTPS.satisfy any;lets either an allowed IP or valid credentials through; the defaultsatisfy allrequires both.auth_request /auth;delegates the decision to a subrequest, letting an external service (SSO proxy) authorize each request.
Response headers that harden browsers, added with add_header ... always; so they appear on error pages too:
Strict-Transport-Security(HSTS),X-Content-Type-Options nosniff,Content-Security-Policy,Referrer-Policy, andX-Frame-Optionsor CSPframe-ancestorsagainst clickjacking.
server_tokens off; removes the version number from the Server header and error pages. Remember the add_header inheritance rule: put shared headers in an included snippet and include it wherever a location adds its own headers.
Also limit request size with client_max_body_size (default 1m) so uploads can't exhaust disk.
Example
http {
server_tokens off;
server {
listen 80;
client_max_body_size 10m;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
location /admin/ {
satisfy any; # office IP OR password
allow 203.0.113.0/24;
deny all;
auth_basic "Admin";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:8000;
}
location = /metrics {
allow 10.0.0.0/8; # internal scrapers only
deny all;
proxy_pass http://127.0.0.1:9100;
}
}
}Remember
- allow/deny are checked in order; finish with deny all
- auth_basic needs HTTPS; satisfy any combines IP and password
- Add security headers with always; server_tokens off hides the version
Watch out for
- Forgetting always on add_header, so headers are missing from 4xx and 5xx responses.