PRACTITIONER GUIDE
Practitioner Guide11 min read

nginx Web Server Security Hardening: TLS Configuration, Headers, and Attack Surface Reduction

TLS 1.2 + 1.3
minimum acceptable protocol versions for production nginx; TLS 1.0 and 1.1 must be disabled to eliminate POODLE and BEAST vulnerability exposure
31,536,000
recommended HSTS max-age in seconds (one year); below this value browsers do not cache the HSTS policy long enough to prevent SSL stripping attacks
5 r/m
recommended limit_req_zone rate for login and password reset endpoints; sustained requests above this threshold indicate brute force or credential stuffing
server_tokens off
single nginx directive that removes version number from Server response header and error pages, forcing attackers to fingerprint the version indirectly

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

nginx ships with defaults that prioritize compatibility and ease of installation over security. The version is disclosed in every response header, TLS configuration includes support for older protocol versions, there are no security headers, and the autoindex module will list directory contents if no index file exists. None of these defaults are acceptable for a production server.

Hardening nginx is a configuration discipline: the same nginx binary with a different nginx.conf presents a significantly smaller attack surface. The hardening changes do not require recompiling nginx or installing additional software in most cases, and they do not break correctly written applications.

TLS and header configuration: the high-impact changes

TLS configuration and HTTP security headers deliver the highest security return per line of nginx configuration, and most production servers leave both partially or completely unconfigured. TLS hardening eliminates protocol-level attacks like POODLE and BEAST that exploit older TLS versions, while security headers like HSTS, X-Frame-Options, and Content-Security-Policy give browsers and WAFs the instructions they need to block clickjacking, MIME sniffing, and XSS exploitation. The Mozilla SSL Configuration Generator provides a maintained baseline for TLS settings so practitioners do not need to research cipher suite security properties independently, and a shared security-headers.conf include file ensures consistent header delivery across all server blocks without duplicating directives.

Use the Mozilla SSL Configuration Generator as the TLS baseline

The Mozilla SSL Configuration Generator at ssl-config.mozilla.org produces nginx TLS configuration for three compatibility profiles: Modern (TLS 1.3 only, no legacy client support), Intermediate (TLS 1.2 and 1.3, supports most current clients), and Old (TLS 1.0 and above, for legacy client requirements). Select the Intermediate profile for most production deployments, copy the generated ssl_protocols, ssl_ciphers, and OCSP stapling configuration, and include it in your server block. This baseline is maintained by Mozilla's security team and updated as cipher suite recommendations evolve, removing the need to research cipher suite security properties independently.

Deploy security headers in a shared include file for consistent coverage

Create /etc/nginx/conf.d/security-headers.conf with your security header add_header directives and include it in the http block so the headers apply to all server blocks by default. Include Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy as a baseline. For Content-Security-Policy, deploy in report-only mode first using the Content-Security-Policy-Report-Only header with a report-uri pointing to a CSP violation collection endpoint. Collect violations for two to four weeks before switching to enforcement mode to identify legitimate resources that need to be added to the policy before blocking begins.

Rate limiting and access control: operational protections

Rate limiting authentication endpoints and restricting HTTP methods are the two nginx configuration controls that directly reduce the impact of brute force attacks, credential stuffing, and scanner noise against production servers. The limit_req_zone directive creates a shared memory zone that tracks per-IP request rates across worker processes, and applying separate zones to different endpoint risk tiers avoids the false positives that come from applying a single rate limit uniformly. HTTP method restriction at the server level blocks OPTIONS and TRACE requests that reveal server capabilities to automated scanners, and prevents unexpected methods from reaching application code that may not handle them safely.

Configure separate rate limit zones for different endpoint risk levels

Define multiple limit_req_zone directives in the http block with different rates for different endpoint types. Authentication endpoints (login, password reset, MFA verification) warrant the strictest limits: 5-10 requests per minute per IP. API endpoints that are not authentication-related can use higher limits such as 60 requests per minute. Static asset endpoints typically need no rate limiting. Apply each zone to its corresponding location blocks using limit_req with the burst parameter set to allow short legitimate bursts (burst=5 for auth endpoints) and nodelay to reject burst overflow immediately rather than queuing it.

Restrict HTTP methods at the server level as a default-deny baseline

Add an if block in the server context that returns 405 for any HTTP method other than the set your application requires. For a typical web application: if ($request_method !~ ^(GET|HEAD|POST)$) { return 405; }. If your application uses PUT or DELETE for REST API endpoints, add those to the allowed set and restrict them to API-specific location blocks rather than allowing them globally. This blocks OPTIONS requests that reveal allowed methods via CORS headers to scanners, and blocks TRACE requests that can be used in cross-site tracing attacks.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

The bottom line

nginx security hardening is a configuration review that takes a few hours and substantially reduces the attack surface of a production web server. The high-impact changes are server_tokens off, modern TLS configuration from the Mozilla generator, a security header include file with HSTS and X-Content-Type-Options, rate limiting on authentication endpoints, and autoindex off. These changes apply to every nginx deployment regardless of the application it serves. Content-Security-Policy requires application-specific tuning but provides the most meaningful protection against XSS exploitation and should be deployed in report-only mode during initial rollout.

Frequently asked questions

What are the most important nginx security hardening settings?

The highest-impact nginx security settings in order of implementation: set server_tokens off to suppress version disclosure, restrict TLS to 1.2 and 1.3 using ssl_protocols and configure ssl_ciphers to strong ECDHE suites, add security headers including Strict-Transport-Security with a minimum max-age of 31536000, X-Frame-Options DENY or SAMEORIGIN, X-Content-Type-Options nosniff, and Referrer-Policy, implement limit_req_zone rate limiting on authentication endpoints, disable the autoindex module to prevent directory listing, and restrict HTTP methods using an if block to allow only the methods your application requires.

How do I configure TLS correctly on nginx?

A current TLS configuration in nginx sets ssl_protocols to TLSv1.2 TLSv1.3, configures ssl_ciphers using a modern cipher list such as ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384 with ssl_prefer_server_ciphers off so clients can select preferred ciphers, enables ssl_session_cache shared:SSL:10m with ssl_session_timeout 1d for session resumption performance, configures ssl_stapling on with ssl_stapling_verify on for OCSP stapling, and sets resolver to your DNS server address for OCSP lookups. Use the Mozilla SSL Configuration Generator to produce a configuration tuned to your minimum browser support requirement.

How do I add security headers to nginx?

Security headers in nginx are added using the add_header directive in the server or location block. Critical headers include: add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains' always for HSTS, add_header X-Frame-Options DENY always to prevent clickjacking, add_header X-Content-Type-Options nosniff always to prevent MIME sniffing, add_header Referrer-Policy strict-origin-when-cross-origin always for referrer control, and add_header Permissions-Policy interest-cohort=() always. The always parameter ensures the header is sent on all response codes including error responses. Add these to an http context using a separate security-headers.conf include file to apply consistently across all server blocks.

How does nginx rate limiting work and how do I configure it for login endpoints?

nginx rate limiting uses limit_req_zone in the http context to define a shared memory zone keyed on a variable such as $binary_remote_addr with a request rate limit, then limit_req in the location block to apply the zone with optional burst handling. For login endpoints, configure the zone with a strict rate such as 5r/m (5 requests per minute per IP) and apply it with nodelay so burst requests above the limit receive a 503 immediately rather than queuing. Add a limit_req_status 429 directive to return the correct HTTP status code for rate-limited requests. Log rate-limited requests to a dedicated log for monitoring failed login attempt patterns.

How do I hide nginx version information and suppress error page details?

Setting server_tokens off in the http, server, or location context removes the nginx version from the Server response header and from the default error page HTML. To further customize error pages so they do not reveal server technology, use error_page directives to serve custom HTML for common error codes (400, 403, 404, 500, 502, 503) that contain no server identification. If you need to completely remove the Server header rather than just suppress the version, use the nginx-module-headers-more module with more_clear_headers Server, as server_tokens off does not remove the header entirely.

What nginx modules should I disable to reduce attack surface?

Modules to disable or restrict in a production nginx configuration: autoindex (disables directory listing for directories without an index file), dav (WebDAV support rarely needed in application deployments), status if the stub_status module endpoint is exposed publicly (restrict to internal IPs only), and ssi (Server Side Includes, a legacy feature that can enable injection if input reaches SSI directives). For nginx compiled from source, omit unused modules at compile time. For distribution packages, review the loaded modules with nginx -V and use the modules-disabled directory in /etc/nginx to disable dynamic modules that are not required.

How do I configure nginx to block common web attacks?

nginx configuration-level attack blocking covers several patterns: restrict HTTP methods using an if ($request_method !~ ^(GET|HEAD|POST)$) block returning 405 for disallowed methods, block common scanner user-agents using a map directive matching known scanner strings, configure limit_req_zone on the main server to rate limit scanning behavior, use ngx_http_geo_module to block IP ranges if your application has a known geographic user base, and add the X-Content-Type-Options nosniff header to prevent MIME confusion attacks. For application-layer attack blocking such as SQL injection and XSS patterns, a WAF like ModSecurity with the OWASP Core Rule Set provides significantly more coverage than nginx configuration alone.

Sources & references

  1. nginx Security Best Practices
  2. Mozilla SSL Configuration Generator
  3. OWASP Secure Headers Project
  4. CIS nginx Benchmark

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.