Infrastructure Security

NGINX Security Configuration Guide for Production Deployments

NGINX powers a third of the internet. Its default configuration is optimized for getting started, not for production security. Here is the gap.

Alex
Platform Security Lead
4 min read

NGINX serves roughly a third of the world's websites and is the most popular reverse proxy for containerized applications. Its configuration flexibility is both its strength and its risk. A stock NGINX installation works, but it leaks information, accepts weak TLS, and lacks basic security controls.

This guide covers the configurations that transform NGINX from a functional web server into a hardened security boundary.

TLS Hardening

Disable Old Protocols

TLS 1.0 and 1.1 have known vulnerabilities and are deprecated by all major browsers. Allow only TLS 1.2 and 1.3:

ssl_protocols TLSv1.2 TLSv1.3;

Configure Strong Cipher Suites

Default cipher suites often include weak options for backward compatibility. Restrict to modern, secure ciphers:

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;

Enable OCSP Stapling

OCSP stapling lets NGINX provide certificate revocation status directly to clients, improving both performance and privacy:

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

Configure Session Security

TLS session tickets can leak session keys if the server is compromised. Disable them or rotate the ticket key regularly:

ssl_session_tickets off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;

Security Headers

HTTP Strict Transport Security

HSTS tells browsers to always use HTTPS for your domain:

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

The always parameter ensures the header is sent even on error responses.

Content Security Policy

CSP prevents cross-site scripting by controlling which resources can load on your pages:

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;

Other Essential Headers

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

Information Leakage Prevention

Hide Server Version

By default, NGINX announces its version in the Server header and on error pages. Remove this information:

server_tokens off;

For NGINX Plus or with the headers-more module, you can remove the Server header entirely:

more_clear_headers Server;

Custom Error Pages

Default error pages reveal that you are running NGINX and can include version information. Replace them with custom pages:

error_page 404 /custom_404.html;
error_page 500 502 503 504 /custom_50x.html;

Restrict Sensitive Paths

Block access to hidden files, version control directories, and configuration files:

location ~ /\. {
    deny all;
    return 404;
}

location ~ ~$ {
    deny all;
    return 404;
}

Rate Limiting

Define Rate Limit Zones

Create rate limit zones based on client IP:

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

Apply Rate Limits Per Location

location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://backend;
}

location /auth/login {
    limit_req zone=login burst=3;
    proxy_pass http://auth_backend;
}

The login endpoint has a much stricter rate limit than general API endpoints.

Connection Limiting

In addition to request rate limiting, limit concurrent connections:

limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
    limit_conn addr 10;
}

Request Size and Timeout Controls

Limit Request Body Size

Prevent denial-of-service through large uploads:

client_max_body_size 10m;

Configure Timeouts

Prevent slow-loris and similar attacks:

client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;

Limit Buffer Sizes

Restrict buffer sizes to prevent buffer overflow exploitation:

client_body_buffer_size 1k;
client_header_buffer_size 1k;
large_client_header_buffers 2 1k;

Access Control

IP-Based Restrictions

Restrict administrative endpoints to trusted networks:

location /admin {
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://admin_backend;
}

Basic Authentication

For internal tools that do not have their own authentication:

location /internal {
    auth_basic "Restricted Access";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://internal_backend;
}

Reverse Proxy Security

Sanitize Upstream Headers

When proxying requests, control which headers are forwarded to backends and which backend headers are returned to clients:

proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Prevent SSRF Through Proxy

If NGINX proxies to user-specified URLs (which should be avoided when possible), validate and restrict the upstream addresses to prevent server-side request forgery.

Logging

Enable Detailed Access Logging

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

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

Enable Error Logging

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

Forward both access and error logs to your SIEM for security monitoring and incident investigation.

How Safeguard.sh Helps

Safeguard.sh monitors the security of your NGINX deployments by scanning NGINX container images for known vulnerabilities, tracking the modules and dependencies compiled into your NGINX build, and generating SBOMs that provide complete visibility into your web server infrastructure. When a CVE affects NGINX or one of its modules, Safeguard.sh identifies every instance in your environment that needs patching.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.