# Access Control, Authentication and Security Headers — Nginx

Source: https://www.skillbyai.com/en/nginx/x-access

> Restrict access and add protective response headers.

## Layers of protection at the edge

**`allow`** and **`deny`** restrict locations by IP address or CIDR range, ideal for admin panels and internal endpoints (`allow 10.0.0.0/8; deny all;`). **`auth_basic`** with an `htpasswd` file adds simple password protection, acceptable for internal tools over HTTPS. **`auth_request`** delegates authentication to a subrequest: NGINX calls an internal auth service and allows the request only if it returns 2xx, which is how many setups integrate single sign-on proxies such as oauth2-proxy. **Security headers** reduce browser-side attacks: `X-Content-Type-Options: nosniff`, `Referrer-Policy`, a `Content-Security-Policy` tailored to your app, `X-Frame-Options` or CSP `frame-ancestors` against clickjacking, and HSTS. Use the `always` parameter so headers are added to error responses too. **`server_tokens off;`** hides the NGINX version in headers and error pages. Block access to hidden files such as `.git` and `.env`, which are frequently exposed by mistake.

## Protecting admin, hiding secrets and adding headers

A reusable security snippet plus location-level rules.

```nginx
# /etc/nginx/snippets/security-headers.conf
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:" always;

server {
    server_tokens off;
    include snippets/security-headers.conf;

    location ~ /\.(?!well-known) { deny all; }      # .git, .env, .htpasswd ...

    location /admin/ {
        allow 10.0.0.0/8;
        deny all;
        auth_basic "Admin";
        auth_basic_user_file /etc/nginx/.htpasswd;
        include snippets/security-headers.conf;     # repeat: add_header is not merged
        proxy_pass http://app_backend;
    }

    location /internal-dashboard/ {
        auth_request /oauth2/auth;                  # SSO via an auth subrequest
        proxy_pass http://dashboard;
    }
    location = /oauth2/auth {
        internal;
        proxy_pass http://oauth2_proxy;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
    }
}
```

## Security guards at different doors

The front door checks bags (headers), the staff entrance checks ID badges (allow/deny), the server room asks for a PIN (auth_basic) and the executive floor phones head office to confirm (auth_request).

**Quiz:** Why add the `always` parameter to add_header for security headers?

- [ ] To make the header apply to all servers
- [ ] To make the header case-insensitive
- [ ] To disable caching
- [x] So the header is also added to error responses such as 404 and 500

*Answer:* So the header is also added to error responses such as 404 and 500. Without always, add_header applies only to successful and redirect responses.
