DEV Community

Ramer Labs
Ramer Labs

Posted on

7 Tips for Securing Nginx with TLS and Firewall on Production Servers

Overview

Running Nginx in a production environment means you’re the front line of your web traffic. A mis‑configured server can expose sensitive data, invite DDoS attacks, or leak internal version numbers. This checklist walks a DevOps lead through the essential hardening steps—TLS, firewall rules, and a few often‑overlooked tweaks—so you can lock down the stack without sacrificing performance.


1️⃣ Get a Valid TLS Certificate

A self‑signed cert is fine for internal testing, but browsers and API clients expect a trusted certificate chain. The easiest way to obtain one is Let’s Encrypt:

# Install Certbot (Debian/Ubuntu example) sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx # Request a cert for example.com and www.example.com sudo certbot --nginx -d example.com -d www.example.com 
Enter fullscreen mode Exit fullscreen mode

Certbot will automatically edit your Nginx config, add a listen 443 ssl; block, and set up a renewal cron job. Verify renewal works with sudo certbot renew --dry-run.


2️⃣ Harden SSL/TLS Settings

Modern browsers support TLS 1.3, which offers better security and lower latency. Disable older protocols and weak ciphers in a dedicated ssl.conf snippet:

# /etc/nginx/snippets/ssl.conf 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"; # Enable HTTP/2 for speed listen 443 ssl http2; # HSTS (force HTTPS for a year) add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; 
Enter fullscreen mode Exit fullscreen mode

Include the snippet in each server block: include snippets/ssl.conf;. Remember to reload Nginx after changes: sudo systemctl reload nginx.


3️⃣ Hide Server Identity

By default Nginx reveals its version in the Server header, which gives attackers a foothold for known exploits. Turn it off and replace it with a generic value:

# In http { } server_tokens off; more_clear_headers Server; # requires ngx_headers_more module 
Enter fullscreen mode Exit fullscreen mode

If you don’t have ngx_headers_more, you can simply set server_tokens off;—most scanners will still see nginx but not the exact version.


4️⃣ Implement Host‑Based Firewall Rules

A properly scoped firewall blocks everything except the ports you actually need. On Ubuntu/Debian, ufw is straightforward:

sudo ufw default deny incoming sudo ufw default allow outgoing # Allow HTTP/HTTPS from anywhere sudo ufw allow 80/tcp sudo ufw allow 443/tcp # SSH limited to your admin IP (replace with your IP) sudo ufw allow from 203.0.113.42 to any port 22 proto tcp # Enable the firewall sudo ufw enable 
Enter fullscreen mode Exit fullscreen mode

If you’re on a cloud provider, also lock down the security group to the same ports.


5️⃣ Deploy Fail2Ban for Brute‑Force Protection

Fail2Ban watches Nginx logs for repeated 4xx/5xx patterns and bans the offending IP via iptables. Install and enable a ready‑made filter:

sudo apt-get install -y fail2ban # Create a jail for Nginx HTTP auth failures cat <<EOF | sudo tee /etc/fail2ban/jail.d/nginx-http-auth.conf [nginx-http-auth] enabled = true filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 5 bantime = 3600 EOF sudo systemctl restart fail2ban 
Enter fullscreen mode Exit fullscreen mode

You can add custom filters for rate‑limit bypass attempts or malformed request strings.


6️⃣ Enable Rate Limiting at the Server Level

Even with a firewall, a sudden spike can overwhelm your upstream services. Nginx’s limit_req_zone and limit_req directives throttle abusive clients:

# Define a shared memory zone (10 MB can hold ~160k states) limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; server { listen 443 ssl http2; ... location /api/ { limit_req zone=mylimit burst=20 nodelay; proxy_pass http://backend; } } 
Enter fullscreen mode Exit fullscreen mode

Adjust rate and burst based on your traffic profile. Combine with limit_conn if you need to cap concurrent connections per IP.


7️⃣ Test, Monitor, and Iterate

Hardening is a continuous process. Use these tools to verify you didn’t break anything:

  • SSL Labs – Run a quick external scan of your domain to confirm protocol/cipher settings.
  • curl – Check headers locally: curl -I https://example.com should show Strict-Transport-Security and no Server version.
  • Prometheus + Grafana – Export Nginx metrics (stub_status) and watch for spikes in 4xx/5xx rates.
  • Logwatch – Set up daily summaries of firewall denials and Fail2Ban bans.

When you spot a false positive (e.g., a legitimate IP getting blocked), fine‑tune the maxretry or whitelist the address in /etc/fail2ban/jail.local.


Wrap‑up

By following these seven steps—trusted TLS, stripped server fingerprints, tight firewall rules, automated brute‑force mitigation, and smart rate limiting—you’ll dramatically reduce the attack surface of any Nginx‑powered service. Remember, security is a habit, not a one‑time checklist. For deeper dives into Linux hardening and cloud‑native monitoring, you might find the resources at https://lacidaweb.com useful as you continue to refine your production pipeline.

Top comments (0)