# How to Harden Nginx Web Server Configuration: 8 Essential Security Measures

> Harden your Nginx web server with 8 essential security measures. Learn to disable version tokens, enforce TLS, implement security headers, rate-limit requests, and run as non-root for better protection.

- Repository: [Michał Ży/the-book-of-secret-knowledge](https://github.com/trimstray/the-book-of-secret-knowledge)
- Tags: how-to-guide
- Published: 2026-02-24

---

**Harden your Nginx web server by disabling version tokens, enforcing TLS 1.2/1.3 with modern cipher suites, implementing strict HTTP security headers, rate-limiting requests, and running the service as a non-root user.**

The trimstray/the-book-of-secret-knowledge repository curates battle-tested resources for system administrators, including the authoritative NGINX Admin's Handbook which provides definitive guidance on how to harden Nginx web server installations against modern attack vectors. This guide synthesizes those recommendations with current CIS Benchmarks and Mozilla SSL standards to provide actionable, copy-paste security configurations that reduce your attack surface.

## Reduce Information Exposure

Default Nginx installations leak version numbers and module details that aid reconnaissance attacks. According to the trimstray/the-book-of-secret-knowledge security references, the first step in any hardening regimen is eliminating this information disclosure.

Disable the `server_tokens` directive to prevent Nginx from appending version numbers to error pages and response headers:

```nginx
server_tokens off;

```

For Nginx Plus builds or installations with the Headers More module, you can further sanitize responses using `more_clear_headers` to strip identifying headers entirely.

## Enforce Modern TLS and SSL Configuration

Transport Layer Security (TLS) configuration is critical to prevent man-in-the-middle attacks and protocol downgrade attempts. The NGINX Admin's Handbook recommends explicit protocol and cipher controls rather than relying on defaults.

Explicitly allow only TLS 1.2 and TLS 1.3, disabling legacy SSL and early TLS versions vulnerable to POODLE and BEAST attacks:

```nginx
ssl_protocols TLSv1.2 TLSv1.3;

```

Configure a modern cipher suite prioritizing Elliptic Curve Diffie-Hellman Ephemeral (ECDHE) key exchange and AES-GCM authenticated encryption:

```nginx
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;

```

Enable **OCSP stapling** to improve revocation checking performance and privacy:

```nginx
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;

```

Implement **HTTP Strict Transport Security (HSTS)** to force HTTPS connections and prevent SSL stripping attacks:

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

```

## Implement Security Headers

HTTP security headers provide browser-level protections against XSS, clickjacking, and MIME-sniffing attacks. As documented in the hardening resources, these headers should be set globally in the `http` block to apply to all virtual hosts.

Deploy a **Content Security Policy (CSP)** to mitigate cross-site scripting and data injection:

```nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted.cdn.com; object-src 'none';";

```

Add protective headers to prevent clickjacking and content type sniffing:

```nginx
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy no-referrer-when-downgrade;

```

## Limit Request Size and Implement Rate Limiting

Unrestricted request handling enables Denial-of-Service (DoS) attacks and brute-force attempts. The hardening guide recommends strict resource limits.

Cap upload sizes to prevent memory exhaustion:

```nginx
client_max_body_size 1M;

```

Define rate limiting zones to throttle abusive clients. This configuration creates a 10MB zone tracking binary remote addresses, allowing 10 requests per second with burst capacity:

```nginx
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

server {
    limit_req zone=mylimit burst=20 nodelay;
}

```

## Restrict Access to Sensitive Locations

File system exposure and unnecessary HTTP methods create privilege escalation risks. Restrict access to hidden files (dotfiles) and administrative endpoints.

Block access to hidden files and version control directories while preserving `.well-known` for ACME challenges:

```nginx
location ~ /\.(?!well-known) {
    deny all;
}

```

Disable unsafe HTTP methods, allowing only GET, HEAD, and POST as required:

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

```

## Run Nginx as an Unprivileged User

Executing Nginx as root creates severe privilege escalation risks if a worker process is compromised. The source code analysis emphasizes using a dedicated non-root user.

Specify an unprivileged service account in the main configuration:

```nginx
user nginx;

```

Ensure that configuration files in `/etc/nginx/` are owned by root and readable only by the service user (mode 640), preventing unauthorized modifications.

## Enable Comprehensive Logging and Monitoring

Security forensics require detailed transaction logs and real-time monitoring capabilities. The trimstray repository references `ngxtop` as an essential monitoring utility for Nginx environments.

Configure detailed access and error logging with appropriate severity levels:

```nginx
access_log /var/log/nginx/access.log main;
error_log  /var/log/nginx/error.log warn;

```

Integrate **ngxtop** (available at `lebinh/ngxtop` on GitHub) for real-time log analysis, request rate monitoring, and status code distribution tracking. Export logs to a SIEM for centralized correlation and alerting.

## Apply OS-Level Hardening

Nginx security extends beyond application configuration to the underlying operating system. Complement your hardening with system-level controls.

- **Maintain updated binaries**: Use the latest stable Nginx release to receive security patches
- **Configure host-based firewalls**: Restrict inbound traffic to ports 80 and 443 using `ufw` or `iptables`
- **Enable Mandatory Access Control**: Apply SELinux or AppArmor profiles to confine Nginx processes and limit file system access

## Production-Ready Configuration Example

The following consolidated [`nginx.conf`](https://github.com/trimstray/the-book-of-secret-knowledge/blob/main/nginx.conf) implements the critical hardening measures discussed above. Adapt paths, domain names, and CSP directives to match your specific environment:

```nginx
user nginx;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 1024;
    multi_accept on;
}

http {
    # Security headers

    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy no-referrer-when-downgrade;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none'";

    # TLS hardening

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s;

    # Information disclosure

    server_tokens off;

    # Request limits

    client_max_body_size 1M;
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

    # Logging

    access_log /var/log/nginx/access.log combined;
    error_log  /var/log/nginx/error.log warn;

    server {
        listen 80 default_server;
        listen 443 ssl http2 default_server;
        server_name _;

        # HTTP to HTTPS redirect

        if ($scheme = http) {
            return 301 https://$host$request_uri;
        }

        # Rate limiting

        limit_req zone=mylimit burst=20 nodelay;

        # Block hidden files

        location ~ /\.(?!well-known) {
            deny all;
        }

        root /usr/share/nginx/html;
        index index.html index.htm;
    }
}

```

## Summary

- **Disable information leakage** by setting `server_tokens off` and hiding version headers to prevent reconnaissance attacks
- **Enforce TLS 1.2/1.3 only** with strong cipher suites (`ECDHE` + `AES-GCM`), OCSP stapling, and HSTS headers to secure data in transit
- **Deploy security headers** including CSP, X-Frame-Options, and X-Content-Type-Options to protect against XSS and clickjacking
- **Implement rate limiting** using `limit_req_zone` and restrict request sizes with `client_max_body_size` to mitigate DoS attempts
- **Run as non-root** using the `user nginx` directive and strict file permissions (640) on configuration files
- **Monitor actively** with `ngxtop` and detailed access logs for security forensics and real-time threat detection

## Frequently Asked Questions

### How do I verify my Nginx hardening configuration is working?

Test your configuration using online scanners like the Qualys SSL Labs Server Test and security headers inspection tools. Run `nginx -t` to validate syntax before reloading, then inspect response headers using `curl -I https://yourdomain.com` to confirm `server_tokens` is off and security headers are present. For TLS verification, use `openssl s_client -connect yourdomain.com:443 -tls1_1` to confirm rejected connections on older protocols.

### What is the difference between `server_tokens off` and using the Headers More module?

`server_tokens off` is a core Nginx directive that removes the version number from the `Server` header and error pages, but leaves the header itself showing "nginx". The Headers More module (via `more_clear_headers Server`) can completely remove the `Server` header or modify any response header, providing more comprehensive information hiding but requiring third-party module compilation or Nginx Plus.

### Should I use Mozilla's Modern or Intermediate SSL configuration for Nginx?

Use the **Modern** configuration only if you can drop support for older clients (IE11 on Windows 7/8, older Android devices). The **Intermediate** configuration provides broader compatibility while maintaining strong security. According to the NGINX Admin's Handbook, production environments typically use Intermediate with explicit cipher lists like `'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256'` to balance security and accessibility.

### How does rate limiting in Nginx prevent brute-force attacks?

The `limit_req_zone` directive creates a shared memory zone tracking request rates per IP address (`$binary_remote_addr`). When a client exceeds the defined rate (e.g., 10 requests per second), Nginx delays or rejects subsequent requests with a 503 error. This prevents automated tools from rapidly attempting authentication or exploiting endpoints, effectively neutralizing credential stuffing and basic DoS attacks without blocking legitimate traffic entirely.