Access control and security headers

NGINX · Advanced · 10 min

Admin panels, metrics endpoints and staging sites need protection at the edge, and headers harden every page.

NGINX has several simple access controls:

  • allow and deny (access module) check the client address in order; the first match wins. End with deny all;.
  • auth_basic "Restricted"; with auth_basic_user_file asks for a username and password, stored in an htpasswd file (create it with htpasswd from apache2-utils or openssl passwd). Only use it over HTTPS.
  • satisfy any; lets either an allowed IP or valid credentials through; the default satisfy all requires 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, and X-Frame-Options or CSP frame-ancestors against 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.

Check yourself

  1. With satisfy any, allow 10.0.0.0/8, deny all and auth_basic, who gets in?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: nginx.org/en/docs/http/ngx_http_access_module.html