NGINX Hardening Checklist 2025

A comprehensive security checklist for production NGINX servers. 40+ configurations covering version disclosure, TLS, headers, access control, and more.

Updated: January 2025 15 min read

1. Information Disclosure

Prevent attackers from fingerprinting your server.

Hide NGINX version Critical

Prevents attackers from targeting version-specific vulnerabilities.

# In http block
server_tokens off;

Remove Server header entirely Recommended

Requires the headers-more module or NGINX Plus.

# With headers-more-nginx-module
more_clear_headers Server;

Disable autoindex Important

Prevents directory listing that could expose sensitive files.

autoindex off;

2. TLS/SSL Configuration

Modern TLS settings for maximum security.

Use TLS 1.2 and 1.3 only Critical

TLS 1.0 and 1.1 have known vulnerabilities and are deprecated.

ssl_protocols TLSv1.2 TLSv1.3;

Configure modern cipher suites Critical

Use strong ciphers and prefer server's cipher order.

ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;  # Let client choose for TLS 1.3

Enable OCSP stapling Important

Improves TLS handshake performance and privacy.

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

Configure session caching Recommended

Improves performance for returning visitors.

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;  # Disable for forward secrecy

3. Security Headers

HTTP headers that protect against common attacks.

Enable HSTS Critical

Forces HTTPS and prevents downgrade attacks.

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Prevent clickjacking Critical

Blocks your site from being embedded in iframes.

add_header X-Frame-Options "SAMEORIGIN" always;

Prevent MIME sniffing Important

Forces browsers to respect declared content types.

add_header X-Content-Type-Options "nosniff" always;

Configure Content-Security-Policy Important

Controls which resources can be loaded. Start permissive, then tighten.

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; frame-ancestors 'self';" always;

Control referrer information Recommended

Limits what referrer info is sent to other sites.

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Disable dangerous features Recommended

Restricts access to browser features you don't need.

add_header Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;

4. Access Control

Limit what requests are allowed.

Limit request body size Important

Prevents large uploads that could exhaust resources.

client_max_body_size 10m;  # Adjust based on your needs

Restrict HTTP methods Recommended

Only allow methods your application needs.

if ($request_method !~ ^(GET|HEAD|POST)$) {
    return 405;
}

Block sensitive files Critical

Prevent access to configuration and hidden files.

# Block hidden files
location ~ /\. {
    deny all;
    return 404;
}

# Block common sensitive files
location ~* (\.git|\.env|\.htaccess|\.htpasswd|web\.config)$ {
    deny all;
    return 404;
}

Protect admin areas Important

Restrict access to admin panels by IP or authentication.

location /admin/ {
    allow 10.0.0.0/8;
    allow 192.168.0.0/16;
    deny all;
    
    # Or use basic auth
    # auth_basic "Admin Area";
    # auth_basic_user_file /etc/nginx/.htpasswd;
}

5. Rate Limiting

Protect against abuse and DDoS.

Configure request rate limiting Important

Limit requests per second from each client.

# In http block
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;

# In server/location
limit_req zone=req_limit burst=20 nodelay;
limit_req_status 429;

Limit concurrent connections Recommended

Prevent a single IP from opening too many connections.

# In http block
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

# In server/location
limit_conn conn_limit 20;
limit_conn_status 429;

6. Logging & Monitoring

Visibility into what's happening on your server.

Enable access logging Critical

Log all requests for security analysis.

access_log /var/log/nginx/access.log combined buffer=512k flush=1m;

Enable error logging Critical

Capture errors for debugging and security monitoring.

error_log /var/log/nginx/error.log warn;

Log security-relevant headers Recommended

Include useful info in access logs.

log_format security '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '"$http_x_forwarded_for" $request_time';

access_log /var/log/nginx/security.log security;

7. Miscellaneous

Set proper timeouts Recommended

Prevent slowloris attacks and resource exhaustion.

client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 65s;
send_timeout 10s;

Limit buffer sizes Recommended

Prevent buffer overflow attacks.

client_body_buffer_size 16k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;

Run Gixy regularly Critical

Automate security scanning in your CI/CD pipeline.

# Install and run
pip install gixy-ng
gixy /etc/nginx/nginx.conf

Complete Example Configuration

Here's a minimal secure server block incorporating all critical items:

server {
    listen 443 ssl http2;
    server_name example.com;
    
    # TLS
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    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_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # Hide version
    server_tokens off;
    
    # Limits
    client_max_body_size 10m;
    
    # Block hidden files
    location ~ /\. {
        deny all;
        return 404;
    }
    
    # Your application
    location / {
        # ...
    }
}