Skip to main content

Nginx security best practices 2026

TL;DR

Complete guide to hardening nginx with security headers, TLS configuration, rate limiting, and access controls for production web servers.

Key facts

Topology
Nginx security hardening

TL;DR

Nginx's default configuration prioritises compatibility over security. A production server needs version hiding, strict security headers, modern TLS, rate limiting, and access controls. This guide covers each hardening step with copy-paste configuration blocks you can apply immediately.

Hide the server version

The Server header broadcasts your nginx version to every client, giving attackers a head start:

http {
    server_tokens off;
}

Verify after reloading:

curl -I https://example.com | grep -i server
# Server: nginx

Security headers

Add these to your server block or a shared include file. Each header closes a specific attack vector:

# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;

# Stop MIME-type sniffing
add_header X-Content-Type-Options "nosniff" always;

# Control referrer information
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# HTTP Strict Transport Security — force HTTPS for 1 year including subdomains
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# Restrict browser features
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

# Content Security Policy — adjust to match your application
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'self';" always;

TLS configuration

Use TLS 1.2 and 1.3 only with strong cipher suites. Disable TLS 1.0 and 1.1 entirely:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;

Enable OCSP stapling so clients can verify your certificate without contacting the CA:

ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

Rate limiting

Protect against brute force and denial-of-service at the nginx level:

http {
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=3r/m;
}

server {
    location / {
        limit_req zone=general burst=20 nodelay;
    }

    location /login {
        limit_req zone=login burst=5 nodelay;
        proxy_pass http://backend;
    }
}

The login zone allows only 3 requests per minute per IP — enough for legitimate users but punishing for brute-force scripts.

Block bad user agents

Many scanners and bots identify themselves. Block them early:

if ($http_user_agent ~* (sqlmap|nikto|masscan|zgrab|python-requests|Go-http-client)) {
    return 444;
}

Status 444 is an nginx-specific code that drops the connection without sending a response.

Prevent directory listing

Ensure autoindex is off (it is by default, but verify):

autoindex off;

Block direct IP access

If someone hits your server by IP instead of domain name, return nothing:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    ssl_certificate     /etc/ssl/certs/default.pem;
    ssl_certificate_key /etc/ssl/private/default.key;
    server_name _;
    return 444;
}

Request size and timeout limits

Prevent oversized uploads and slow-loris attacks:

client_max_body_size 10m;
client_body_timeout 12s;
client_header_timeout 12s;
send_timeout 10s;
keepalive_timeout 65s;

Adjust client_max_body_size to match your application's actual upload requirements.

Test and reload

Always validate before reloading:

sudo nginx -t && sudo systemctl reload nginx

Verify headers are applied:

curl -I https://example.com

With Reflex

Reflex monitors your nginx configuration for security regressions — missing headers, weakened TLS settings, or accidentally removed rate limits — and alerts you when a configuration change introduces a vulnerability. See How it works.