Introduction
Running Nginx on a public server without proper hardening is a recipe for abuse. Two of the most effective, low‑maintenance defenses are TLS encryption and Fail2Ban. TLS protects data in transit, while Fail2Ban automatically bans IPs that show malicious behavior (e.g., repeated 404s or brute‑force login attempts). This tutorial walks an SRE through a practical, step‑by‑step hardening of an Ubuntu 22.04 Nginx instance.
Why TLS and Fail2Ban Matter for Nginx
- TLS eliminates eavesdropping and man‑in‑the‑middle attacks. Modern browsers also flag non‑HTTPS sites as insecure, hurting user trust.
- Fail2Ban watches log files for patterns that indicate attacks (failed logins, repeated 404s, etc.) and adds temporary firewall rules to block the offending IPs.
- Together they address both data confidentiality and automated abuse, two common vectors in today's threat landscape.
Prerequisites
- Ubuntu 22.04 server with root or sudo access.
- Nginx installed (
sudo apt install nginx
). - A domain name pointing to the server's public IP.
- Basic familiarity with
systemctl
and editing files under/etc
.
1. Install and Configure TLS with Let’s Encrypt
Install Certbot
sudo apt update && sudo apt install -y certbot python3‑certbot‑nginx
Obtain a Certificate
Run Certbot in Nginx mode; it will automatically edit your virtual host files:
sudo certbot --nginx -d example.com -d www.example.com
Answer the prompts (agree to the terms, choose Redirect to enforce HTTPS). Certbot will:
- Request a certificate from Let’s Encrypt.
- Install the certificate at
/etc/letsencrypt/live/...
. - Add a
listen 443 ssl;
block and a redirect from port 80.
Verify Renewal
sudo systemctl status certbot.timer
The timer runs twice daily; you can also test a dry‑run:
sudo certbot renew --dry-run
2. Harden TLS Cipher Suites
Open the default SSL snippet (or create one) at /etc/nginx/snippets/ssl-params.conf
and paste the following:
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:EECDH+AESGCM:EDH+AESGCM";
ssl_ecdh_curve secp384r1;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
Then include this snippet in each server block:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
include snippets/ssl-params.conf;
# ... other directives ...
}
Reload Nginx:
sudo systemctl reload nginx
3. Install Fail2Ban and Create an Nginx Jail
sudo apt install -y fail2ban
Create a local jail configuration at /etc/fail2ban/jail.local
(do not edit jail.conf
directly):
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600
findtime = 600
[nginx-botsearch]
enabled = true
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 5
bantime = 7200
findtime = 900
Fail2Ban ships with the required filters (nginx-http-auth.conf
and nginx-botsearch.conf
). Restart the service:
sudo systemctl restart fail2ban
Check the status of the new jails:
sudo fail2ban-client status nginx-http-auth
sudo fail2ban-client status nginx-botsearch
4. Tighten the Host Firewall with UFW
If you aren't already using UFW, enable it and allow only the necessary ports:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH # port 22
sudo ufw allow 'Nginx Full' # ports 80 and 443
sudo ufw enable
UFW will now respect the temporary bans Fail2Ban adds via iptables
.
5. Testing and Verification
- TLS sanity check – Visit https://www.ssllabs.com/ssltest/ and run a scan on your domain. Aim for an "A" rating.
- Fail2Ban simulation – From another machine, attempt a protected location:
curl -I https://example.com/protected
Repeat with wrong credentials until the IP is banned (check sudo fail2ban-client status nginx-http-auth
).
-
UFW rules – Verify that the
ufw status numbered
output shows the expected allow rules and that Fail2Ban inserted a rule likeREJECT
for the offending IP.
6. Common Pitfalls and How to Avoid Them
- Certificate renewal failures – Ensure port 80 is reachable for the HTTP‑01 challenge. If you forced a permanent redirect, add a temporary exception in the Nginx config before renewal.
-
Over‑aggressive Fail2Ban bans – Start with a low
bantime
and monitor/var/log/fail2ban.log
. Adjustmaxretry
andfindtime
to suit your traffic pattern. - Cipher suite incompatibility – Older browsers may not support TLS 1.3. Keep TLS 1.2 enabled and test with a range of devices.
-
UFW and Docker conflict – If you later add Docker containers, remember Docker manipulates
iptables
directly; you may need to add explicitufw allow
rules for container ports.
Conclusion
By combining Let’s Encrypt TLS, a hardened cipher suite, Fail2Ban, and a minimal UFW rule set, you dramatically raise the security bar of any Nginx server without sacrificing performance. The steps above are repeatable across multiple hosts, making them ideal for a fleet of web services. For more practical guides and a curated list of security‑focused tools, check out https://lacidaweb.com – it’s a handy reference for busy SREs looking to stay ahead of the curve.
Top comments (0)