Nginx Security Hardening
By default, Nginx serves content securely, but modern threats demand extra layers of protection. This guide walks you through every recommended tweak: from HTTP/2 to strict TLS, HSTS, security headers, and hiding server details. Apply these changes to lock down your web server against clickjacking, downgrade attacks, information leakage, and more.
Key Goals
- Activate HTTP/2 for multiplexing and header compression.
- Enforce TLS 1.2 & 1.3 and strong cipher suites.
- Enable HSTS to force HTTPS on repeat visits.
- Set security headers (CSP, X‑Frame‑Options, X‑Content‑Type‑Options, Referrer‑Policy).
- Hide Nginx version to reduce fingerprinting.
Why It Matters
Without hardening, attackers can exploit:
- Protocol Downgrade: forcing clients to use weaker SSL/TLS versions.
- Clickjacking: framing your site in malicious iframes.
- MIME Sniffing: injesting content with incorrect MIME types.
- Information Leakage: revealing Nginx versions & internal paths.
Prerequisites
- Nginx installed and serving your site over HTTPS (e.g. via Certbot).
- Sudo or root access on your server.
- Backup of your existing Nginx configuration.
Before You Begin
- Backup your config:
sudo cp /etc/nginx/sites-available/default ~/nginx-default.bak - Open your site config in an editor:
sudo nano /etc/nginx/sites-available/default
Step‑by‑Step Hardening
1. Enable HTTP/2
Add http2 to your SSL listeners:
listen 443 ssl http2;
listen [::]:443 ssl http2;
2. Enforce Strong TLS Versions & Ciphers
Ensure only TLS 1.2 and 1.3 are allowed, with secure suites:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM';
3. Enable HSTS
Force browsers to use HTTPS for one year, including subdomains:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
4. Add Security Headers
Paste these inside your server { … } block:
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self';" always;
5. Hide Nginx Version
Prevent version info leakage:
server_tokens off;
Complete Secure Configuration
Below is a combined example for /etc/nginx/sites-available/default:
server {
listen 80;
listen [::]:80;
server_name your.domain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your.domain.com;
root /var/www/html;
index index.html;
# SSL certs (Certbot managed)
ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# TLS settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM';
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self';" always;
# Hide version
server_tokens off;
location / {
try_files $uri $uri/ =404;
}
}
Verify & Reload
sudo nginx -t→ should report “syntax is OK”.sudo systemctl reload nginxto apply changes.- Test in browser or via
curl -I https://your.domain.comto confirm headers and HTTP/2.