Introduction
If you’re a DevOps lead tasked with keeping a public‑facing web service reliable and secure, Nginx is often the front‑line reverse proxy you’ll rely on. While Nginx excels at performance, it also needs a solid security posture to survive the modern threat landscape. This tutorial walks you through seven practical steps to lock down Nginx using TLS, Fail2Ban, and a Web Application Firewall (WAF). The focus is on actionable configuration snippets you can drop into your existing setup.
1. Enforce Strong TLS Settings
TLS is the first line of defense. A weak cipher suite or an outdated protocol version can expose you to downgrade attacks.
- Disable SSLv2/SSLv3 and TLS 1.0/1.1
- Prefer TLS 1.3 when the client supports it
- Use a modern cipher suite (e.g.,
TLS_AES_256_GCM_SHA384
) - Enable OCSP stapling to speed up certificate validation
Add the following block to your nginx.conf
or a dedicated tls.conf
file:
ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers \ "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Restart Nginx after editing:
sudo systemctl reload nginx
2. Harden HTTP Headers
Security‑focused headers reduce the attack surface for XSS, click‑jacking, and MIME sniffing.
add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self'" always;
Apply them in a server
block that serves your public site.
3. Limit Request Size and Rate
Large bodies can be abused for DoS attacks, and rapid request bursts may indicate brute‑force attempts.
client_max_body_size 2M; limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; limit_req zone=mylimit burst=20 nodelay;
Adjust the rate
and burst
values based on typical traffic patterns.
4. Deploy ModSecurity as a WAF
ModSecurity provides a rule‑based engine that can block common web exploits (SQLi, XSS, etc.). Install it via your package manager and enable the OWASP Core Rule Set (CRS).
# Ubuntu/Debian example sudo apt-get install libnginx-mod-http-modsecurity sudo apt-get install owasp-modsecurity-crs
Then include the module in Nginx:
modsecurity on; modsecurity_rules_file /etc/modsecurity/crs/crs-setup.conf;
Fine‑tune the CRS by disabling rules that generate false positives for your application.
5. Set Up Fail2Ban for Nginx Logs
Fail2Ban can automatically ban IPs that repeatedly trigger 4xx/5xx responses. Create a jail that watches the Nginx error log.
[nginx-http-auth] enabled = true filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 5 bantime = 3600
Place the file in /etc/fail2ban/jail.d/nginx.conf
and restart Fail2Ban:
sudo systemctl restart fail2ban
You can also add a custom filter (/etc/fail2ban/filter.d/nginx-http-auth.conf
) that matches patterns like "401"
or "403"
.
6. Separate Static and Dynamic Content
Running static assets (images, CSS, JS) from a dedicated location reduces the attack surface of your application backend.
server { listen 443 ssl http2; server_name static.example.com; root /var/www/static; location / { try_files $uri $uri/ =404; } # Re‑use TLS block from earlier include /etc/nginx/conf.d/tls.conf; }
By isolating static content, you can apply stricter rate limits and even serve it from a CDN.
7. Automate Certificate Renewal
Manual certificate updates are a recipe for downtime. Use certbot with a systemd timer to keep your certs fresh.
sudo apt-get install certbot python3-certbot-nginx sudo certbot renew --dry-run
Create a timer if your distro doesn’t provide one:
[Unit] Description=Renew Let's Encrypt certificates [Timer] OnCalendar=weekly Persistent=true [Service] Type=oneshot ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
Place the file at /etc/systemd/system/certbot-renew.timer
and enable it:
sudo systemctl enable --now certbot-renew.timer
Conclusion
Hardening Nginx is a layered process: strong TLS, security headers, request limiting, a WAF, automated bans, and diligent certificate management all work together to keep your service resilient. Implement these seven steps incrementally, test each change in a staging environment, and monitor logs for unexpected behavior. When you need a quick sanity check or a deeper dive into any of these topics, the community resources at https://lacidaweb.com can provide useful references and real‑world examples.
Top comments (0)