Security Headers and Access Control
Introduction
Nginx sits on the edge of your application—it can add security headers, hide version details, block IP ranges, and password-protect staging areas before traffic reaches Spring Boot or Node. This chapter covers practical hardening without replacing app-level auth or input validation.
Prerequisites
- Serving Static Files
- HTTPS and Let's Encrypt for production headers context
Hide Nginx Version
http {
server_tokens off;
}Response header changes from Server: nginx/1.24.0 to Server: nginx—minor obscurity, not a substitute for patching.
Common Security Headers
server {
listen 443 ssl http2;
server_name example.com;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
}Code explanation:
| Header | Purpose |
|---|---|
X-Frame-Options | Reduce clickjacking (embed in iframe) |
X-Content-Type-Options | Stop MIME sniffing |
Referrer-Policy | Control referer sent on navigation |
Permissions-Policy | Disable browser features you do not use |
Use always so headers appear on error responses too (when supported).
Content-Security-Policy (Concept)
CSP restricts where scripts and styles load from:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;Warning
CSP Breaks Inline Scripts
Strict CSP requires refactored frontend builds. Tune in staging—one wrong directive blocks all JS.
Coordinate with your HTML/JS CSP strategy; Nginx can enforce site-wide baseline.
Strict-Transport-Security (HSTS)
Only on HTTPS after TLS is stable:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;Forces browsers to use HTTPS—see SSL chapter warnings before enabling.
IP Allow and Deny
location /admin/ {
allow 203.0.113.0/24;
allow 198.51.100.10;
deny all;
proxy_pass http://127.0.0.1:8080;
}Code explanation:
allowrules first, thendeny all- Useful for internal admin panels on public VPS
Tip
Cloud WAF
Production teams often add cloud WAF/CDN in front of Nginx—defense in depth.
HTTP Basic Authentication
Protect staging site:
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd staginglocation / {
auth_basic "Staging";
auth_basic_user_file /etc/nginx/.htpasswd;
root /var/www/staging;
}Code explanation:
- Not a replacement for app login—simple gate for previews
- Use strong passwords; restrict
.htpasswdpermissions (640, root)
Block Sensitive Files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~* \.(env|git|sql|bak)$ {
deny all;
}Prevent accidental deployment of .git or .env into web roots.
Limit Request Methods
if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS)$ ) {
return 405;
}Use sparingly—prefer app-level validation. Some APIs need OPTIONS for CORS preflight.
TLS and Cipher Hardening
Rely on Certbot options-ssl-nginx.conf or Mozilla Intermediate profile. Disable TLS 1.0/1.1. Keep Nginx package updated for OpenSSL fixes.
Layered Security Model
| Layer | Nginx can | App must still |
|---|---|---|
| TLS | Terminate HTTPS | Validate auth tokens |
| Headers | Set CSP, HSTS | Correct CORS logic |
| IP allow | Block regions | Authorization checks |
| basic auth | Staging gate | User accounts, RBAC |
FAQ
add_header duplicated in nested locations?
Inner location may omit parent headers—duplicate important headers or use include snippet.
basic auth with API?
Possible but awkward for mobile/SPA—use OAuth/JWT at app layer for APIs.
deny all breaks Let's Encrypt?
HTTP-01 challenge needs port 80 reachable—do not deny all on /.well-known/acme-challenge/:
location ^~ /.well-known/acme-challenge/ {
allow all;
root /var/www/certbot;
}Security headers on static vs API?
Apply at server level for both; tune CSP differently if API returns JSON only (often no CSP needed on JSON responses).
server_tokens off enough?
No—patch OS and Nginx regularly; headers are one small piece.
ModSecurity?
WAF module for Nginx—advanced; optional enterprise hardening.