DEV Community

Ramer Labs
Ramer Labs

Posted on

How to Harden Nginx with TLS, Fail2Ban, and Security Headers

Introduction

As a DevOps lead, you know that a public‑facing web server is the first line of defense against a myriad of attacks. Nginx is fast, flexible, and widely used, but out‑of‑the‑box it lacks a few critical hardening steps. This tutorial walks you through a practical, step‑by‑step hardening checklist that covers:

  • Enforcing strong TLS with modern ciphers
  • Adding a Fail2Ban jail to block brute‑force attempts
  • Sprinkling essential security‑related HTTP headers
  • Automating certificate renewal with certbot

By the end you’ll have a production‑ready Nginx instance that can withstand common web‑layer threats while keeping latency low.


1. Prerequisites

  • Ubuntu 22.04 LTS (or any recent Debian‑based distro)
  • Root or sudo access
  • A domain name pointing to the server’s public IP
  • Basic familiarity with systemd and apt

Tip: If you’re running Nginx inside Docker, the same configuration snippets apply; just copy them into your container’s nginx.conf and rebuild.


2. Install Nginx and Certbot

sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx
Enter fullscreen mode Exit fullscreen mode

Enable the service and verify it’s listening on port 80:

sudo systemctl enable nginx
sudo systemctl start nginx
sudo ss -tlnp | grep :80
Enter fullscreen mode Exit fullscreen mode

3. Enforce TLS 1.2+ with a Secure Cipher Suite

Create a dedicated snippet for TLS settings. This keeps the main server block clean and makes reuse trivial.

# /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers \
    "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
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;
Enter fullscreen mode Exit fullscreen mode

Now reference the snippet in your server block:

server {
    listen 443 ssl http2;
    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;
    include /etc/nginx/snippets/ssl-params.conf;

    # ... your location blocks ...
}
Enter fullscreen mode Exit fullscreen mode

Reload Nginx:

sudo nginx -t && sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

Verify the TLS configuration

Use Qualys SSL Labs or the openssl CLI:

openssl s_client -connect example.com:443 -tls1_2 </dev/null | grep "Cipher"
Enter fullscreen mode Exit fullscreen mode

You should see a modern cipher suite like TLS_AES_256_GCM_SHA384.


4. Deploy Fail2Ban to Thwart Brute‑Force Attacks

Install Fail2Ban and create a custom jail for Nginx:

sudo apt install -y fail2ban
Enter fullscreen mode Exit fullscreen mode

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 = 1h
Enter fullscreen mode Exit fullscreen mode

The built‑in nginx-http-auth filter watches for repeated 401 responses. Restart the daemon:

sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-http-auth
Enter fullscreen mode Exit fullscreen mode

You can also add a generic ssh jail if you haven’t already, tightening the overall surface area.


5. Add Security‑Focused HTTP Headers

Headers such as X‑Content‑Type‑Options, X‑Frame‑Options, and Content‑Security‑Policy mitigate XSS, click‑jacking, and MIME‑sniffing attacks.

Append the following to the same ssl-params.conf snippet (or a separate security-headers.conf if you prefer):

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" 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'; script-src 'self' https://cdnjs.cloudflare.com; object-src 'none';" always;
Enter fullscreen mode Exit fullscreen mode

These headers are sent for both HTTP and HTTPS responses, ensuring consistent protection.


6. Automate Certificate Renewal

Certbot installs a systemd timer that runs twice daily, but it’s good practice to test the renewal flow:

sudo certbot renew --dry-run
Enter fullscreen mode Exit fullscreen mode

If the dry‑run succeeds, you’re set. The timer will automatically reload Nginx after a successful renewal.


7. Optional: Enable Gzip/Brotli for Performance

While security is the focus, a small performance tweak doesn’t hurt. Install the Brotli module (Ubuntu 22.04 ships it as nginx-module-brotli):

sudo apt install -y nginx-module-brotli
Enter fullscreen mode Exit fullscreen mode

Add the following to nginx.conf inside the http block:

brotli on;
brotli_static on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml+rss image/svg+xml;
Enter fullscreen mode Exit fullscreen mode

Reload Nginx again and verify with curl -I -H "Accept-Encoding: br" https://example.com.


8. Monitoring and Alerting

A hardened server is only as good as its visibility. Add a simple Prometheus exporter for Nginx:

sudo apt install -y prometheus-nginx-exporter
Enter fullscreen mode Exit fullscreen mode

Configure the exporter to scrape the /stub_status endpoint (add this location inside your server block):

location = /stub_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}
Enter fullscreen mode Exit fullscreen mode

Now Grafana can surface metrics like nginx_http_requests_total and alert you on spikes in 4xx/5xx responses.


9. Checklist Recap

Hardening Step
1 Install Nginx & Certbot
2 Enforce TLS 1.2+ with strong ciphers
3 Add HSTS and security headers
4 Deploy Fail2Ban with Nginx jail
5 Automate Let's Encrypt renewal
6 (Optional) Enable Brotli compression
7 Set up basic monitoring/alerting

If you tick every box, you’ve dramatically reduced the attack surface while keeping latency low.


10. Closing Thoughts

Hardening Nginx is not a one‑time project; it’s an ongoing discipline. Regularly audit your cipher suite, rotate certificates, and keep both Nginx and Fail2Ban up to date with security patches. When you need a quick reference or a managed solution that follows best practices, consider checking out https://lacidaweb.com for curated hosting options and expert guidance.

Top comments (0)