A default Nginx installation serves content, but many security hardening steps are not enabled by default. Hardening Nginx means configuring HTTP security headers to prevent XSS, clickjacking, and MIME sniffing attacks; enforcing TLS 1.3 and strong cipher suites to eliminate outdated protocol vulnerabilities; enabling OCSP Stapling so clients can verify certificate validity without a round-trip to the CA; hiding server version information; and disabling unused HTTP methods. These measures collectively address OWASP A02 (Cryptographic Failures), A05 (Security Misconfiguration), and harden the server against common web application attacks. This guide covers all major Nginx hardening steps for RHEL 9 with configuration examples you can apply to an existing virtual host.

Prerequisites

  • Nginx with a valid SSL certificate (Let’s Encrypt or self-signed) installed on RHEL 9

Step 1 — Enforce TLS 1.2/1.3 and Strong Ciphers

# Add to the http {} block for global defaults, or per-server {} block
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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers   off;    # TLS 1.3 ignores this; for TLS 1.2 let client order
ssl_session_cache           shared:SSL:10m;
ssl_session_timeout         1d;
ssl_session_tickets         off;    # Disable for Perfect Forward Secrecy

Step 2 — Enable OCSP Stapling

# In the server {} block for HTTPS
ssl_stapling            on;
ssl_stapling_verify     on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver                1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout        5s;

Step 3 — Add HTTP Security Headers

# In the server {} block
# Prevent content type sniffing
add_header X-Content-Type-Options    "nosniff"                 always;

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

# Enable browser XSS filter (for older browsers)
add_header X-XSS-Protection          "1; mode=block"           always;

# Force HTTPS for 2 years, include subdomains
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

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

# Content Security Policy — adjust for your application
add_header Content-Security-Policy   "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; frame-ancestors 'self';" always;

# Permissions Policy (formerly Feature-Policy)
add_header Permissions-Policy        "camera=(), microphone=(), geolocation=()" always;

Step 4 — Hide Server Version and Tokens

# In the http {} block
server_tokens off;   # Don't expose Nginx version in headers or error pages

# Optional: Custom server header via headers_more module
# more_set_headers "Server: webserver";

Step 5 — Disable Unused HTTP Methods

# In the server {} or location {} block
# Allow only GET, HEAD, POST; reject TRACE, DELETE, etc.
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
    return 405;
}

# For APIs that use PUT/DELETE/PATCH, adjust accordingly:
# if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|PATCH)$ ) {

Step 6 — Set Client Buffer Size Limits

# In the http {} block — prevents buffer overflow attacks
client_body_buffer_size   16k;
client_header_buffer_size 1k;
client_max_body_size      8m;
large_client_header_buffers 4 8k;

# Timeout hardening
client_body_timeout   12;
client_header_timeout 12;
keepalive_timeout     15;
send_timeout          10;
nginx -t && systemctl reload nginx

Step 7 — Verify with SSL Labs

# Check headers with curl
curl -I https://example.com/ | grep -iE "strict-transport|x-content|x-frame|content-security"

# Check SSL configuration (requires public domain)
# https://www.ssllabs.com/ssltest/analyze.html?d=example.com

# Local check with openssl
echo | openssl s_client -connect example.com:443 -tls1_3 2>&1 | grep "Protocol|Cipher"

Conclusion

Hardening Nginx on RHEL 9 involves a layered approach: strong TLS configuration eliminates protocol-level vulnerabilities, OCSP Stapling reduces certificate validation latency, HTTP security headers prevent client-side attacks, and rate limiting protects against brute force. Applying these settings brings an Nginx installation to an A+ rating on SSL Labs and substantially reduces the attack surface for common web vulnerabilities.

Next steps: How to Monitor Nginx with Prometheus nginx-exporter on RHEL 9, How to Configure Nginx Rate Limiting on RHEL 9, and How to Secure Apache with Let’s Encrypt and Certbot on RHEL 9.