Introduction
If you’re an SRE or a DevOps lead managing web traffic on Ubuntu servers, you’ve probably heard the mantra "Lock it down before it locks you down". Nginx is a popular front‑end web server, but its default installation leaves a lot of attack surface exposed. In this practical guide we’ll walk through seven concrete steps to harden Nginx with strong TLS settings and a Fail2Ban shield against brute‑force attacks. By the end you’ll have a reproducible, auditable configuration that you can roll out across your fleet.
1. Keep the System Updated
Security starts at the OS level. Run the following commands after every reboot or as part of your CI/CD pipeline:
sudo apt update && sudo apt upgrade -y sudo apt install unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades
Enabling unattended upgrades ensures that critical patches (including OpenSSL updates) are applied automatically.
2. Install Only Required Nginx Modules
A lean binary reduces the code you need to audit. Install the core package plus the http_ssl_module
:
sudo apt install nginx-core libnginx-mod-http-ssl
Avoid the nginx-full
meta‑package unless you truly need extra modules like mail
or stream
.
3. Enforce Strong TLS Settings
Create a dedicated snippet that you can include
in any server block. Save it as /etc/nginx/snippets/strong-ssl.conf
:
# /etc/nginx/snippets/strong-ssl.conf ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers \ "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_stapling on; ssl_stapling_verify on; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block";
Then reference it in your site config:
server { listen 443 ssl http2; server_name example.com; include snippets/strong-ssl.conf; # ... other directives ... }
Run nginx -t
and reload:
sudo nginx -t && sudo systemctl reload nginx
4. Use a Dedicated TLS Certificate Provider
Let’s Encrypt is free and automated, but for production you may want a paid CA that offers OCSP stapling and extended validation. Whichever you choose, store the private key outside the web root and set strict permissions:
sudo chown root:root /etc/ssl/private/example.key sudo chmod 600 /etc/ssl/private/example.key
5. Deploy Fail2Ban to Throttle Malicious Requests
Fail2Ban monitors log files and bans IPs that exceed defined thresholds. Install it and create a jail for Nginx:
sudo apt install fail2ban
Create /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
And the accompanying filter /etc/fail2ban/filter.d/nginx-http-auth.conf
:
[Definition] failregex = ^.*client: <HOST>.*(401|403).*"GET" ignoreregex =
Restart Fail2Ban:
sudo systemctl restart fail2ban
You can verify active bans with sudo fail2ban-client status nginx-http-auth
.
6. Harden Nginx Permissions and Process Isolation
Run Nginx under its own unprivileged user (www-data
by default) and enable a private /tmp
directory:
sudo mkdir -p /var/lib/nginx/tmp sudo chown www-data:www-data /var/lib/nginx/tmp
Add the following to the http
block of nginx.conf
:
http { # ... existing settings ... client_body_temp_path /var/lib/nginx/tmp 1 2; fastcgi_temp_path /var/lib/nginx/tmp 1 2; proxy_temp_path /var/lib/nginx/tmp 1 2; uwsgi_temp_path /var/lib/nginx/tmp 1 2; scgi_temp_path /var/lib/nginx/tmp 1 2; }
7. Enable Monitoring and Automated Alerts
A hardened stack is only as good as the visibility you have into it. Add a simple Prometheus exporter for Nginx:
sudo apt install prometheus-nginx-exporter
Configure the exporter to scrape the /status
endpoint (make sure stub_status
is enabled in a location block). Then set up an alert rule:
# alerts.yml - alert: HighFailedLogins expr: increase(fail2ban_banned_ips_total[5m]) > 10 for: 2m labels: severity: critical annotations: summary: "Too many IPs banned by Fail2Ban" description: "{{ $value }} IPs banned in the last 5 minutes."
Integrate the alerts with your preferred channel (Slack, PagerDuty, etc.).
Conclusion
By following these seven steps you’ve turned a vanilla Nginx install into a hardened, TLS‑only front‑end that actively blocks brute‑force attempts and surfaces security‑related metrics for your monitoring stack. Remember that hardening is an ongoing process: regularly review cipher suites, rotate certificates, and audit Fail2Ban logs.
If you’re looking for more hands‑on tutorials or managed hosting that respects these best practices, give https://lacidaweb.com a look. It offers a developer‑friendly environment with built‑in security defaults.
Top comments (0)