Introduction
Running a public web service on Nginx is great for performance, but it also makes you a target. A single mis‑configuration can expose sensitive data or open the door to brute‑force attacks. This guide walks you through a practical checklist to harden an Nginx instance on a Linux server using TLS, a strict firewall, Fail2Ban, and automated backups. The steps are written for a DevOps lead who wants reproducible, auditable security.
1. Enforce TLS with Modern Cipher Suites
TLS is the first line of defense. Use letsencrypt for free certificates, but the real work is in the Nginx configuration.
# /etc/nginx/conf.d/ssl.conf server { listen 443 ssl http2; server_name example.com www.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # Only TLS 1.2+ and strong ciphers ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH"; # Enable OCSP stapling for faster revocation checks ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # HSTS – tell browsers to always use HTTPS add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Your usual location blocks go here }
- Why these settings? TLS 1.2 and 1.3 drop legacy protocols vulnerable to POODLE and BEAST. The cipher list favours forward secrecy (ECDHE) and AEAD encryption (AES‑GCM). HSTS prevents downgrade attacks.
2. Redirect All HTTP Traffic to HTTPS
A simple server block catches plain HTTP and issues a 301 redirect.
server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }
3. Harden the Firewall (UFW Example)
Only expose ports you need. For a typical web stack, that's 80, 443, and 22 (SSH). Block everything else.
sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow 22/tcp # SSH – consider limiting to your IP range sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable
Tip: Use ufw status numbered
to verify the rule order. Consider moving SSH to a non‑standard port or using key‑based auth only.
4. Deploy Fail2Ban to Thwart Brute‑Force Attempts
Fail2Ban watches Nginx logs for repeated 4xx/5xx responses and bans the offending IP via iptables.
# /etc/fail2ban/jail.d/nginx-http-auth.conf [nginx-http-auth] enabled = true port = http,https filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 5 bantime = 3600 ; 1 hour
Create the matching filter (/etc/fail2ban/filter.d/nginx-http-auth.conf
):
[Definition] failregex = ^<HOST> -.*"GET .* HTTP/.*" 401 ^<HOST> -.*"POST .* HTTP/.*" 401
Restart Fail2Ban:
sudo systemctl restart fail2ban sudo fail2ban-client status nginx-http-auth
5. Secure SSH Access
- Disable password authentication:
PasswordAuthentication no
- Enforce key‑based login:
PubkeyAuthentication yes
- Limit users:
AllowUsers deploy
- Optional: Use
AllowTcpForwarding no
andPermitRootLogin no
.
Apply changes with systemctl reload sshd
.
6. Automated Backups of Certs and Configs
A broken TLS chain is worse than a compromised server. Use rsnapshot
or a simple cron
job to copy /etc/nginx/
, /etc/letsencrypt/
, and /etc/fail2ban/
to a remote storage bucket.
# /etc/cron.daily/nginx-backup #!/bin/bash TIMESTAMP=$(date +%F) DEST="s3://my-backup-bucket/nginx-$TIMESTAMP/" aws s3 sync /etc/nginx $DEST/nginx --delete aws s3 sync /etc/letsencrypt $DEST/letsencrypt --delete aws s3 sync /etc/fail2ban $DEST/fail2ban --delete
Rotate backups with a lifecycle policy (e.g., keep 30 days).
7. Keep the System Patched Automatically
On Debian/Ubuntu, enable unattended upgrades for security patches:
sudo apt-get install unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades
For Red Hat/CentOS, use yum-cron
:
sudo yum install yum-cron sudo systemctl enable --now yum-cron
Regularly audit the /var/log/unattended-upgrades/
directory to ensure updates are applied.
Monitoring and Alerting
Combine Nginx status metrics with Fail2Ban bans in a Prometheus + Grafana stack. A simple exporter can expose nginx_upstream_response_time
and fail2ban_banned_ips
. Set alerts for:
- TLS certificate expiration within 30 days.
- Sudden spikes in 4xx/5xx errors.
- New IP bans exceeding a threshold.
Conclusion
Hardening Nginx is a layered effort: TLS configuration, firewall rules, intrusion‑prevention with Fail2Ban, and disciplined backup and patch processes. By following these seven steps you’ll dramatically reduce the attack surface while keeping your web service performant and reliable. For more hands‑on tutorials and community‑driven best practices, check out https://lacidaweb.com.
Top comments (0)