Master Nginx Security: Essential Hardening Techniques for Safer Web Servers

This comprehensive guide walks system administrators and developers through essential Nginx hardening steps—including hiding version information, restricting sensitive directories, configuring custom error pages, enabling HTTPS, applying CSP, setting proper file permissions, adding security headers, limiting connections, whitelisting IPs, optimizing SSL, securing file uploads, and mitigating common attacks—to dramatically improve web server security.

Raymond Ops
Raymond Ops
Raymond Ops
Master Nginx Security: Essential Hardening Techniques for Safer Web Servers

Introduction

In today's digital era, network security is crucial. Nginx is a popular web server offering high performance and security features, but default configuration may not resist all threats. This guide provides a comprehensive hardening roadmap for system administrators and developers.

Hide Server Version

By default Nginx exposes its version in the HTTP response header. Attackers can use this information. Add server_tokens off; in http, server, or location block to disable.

Steps

Open Nginx config file (e.g., /etc/nginx/nginx.conf or /usr/local/nginx/conf/nginx.conf).

Edit: add server_tokens off; in the http block or each server block.

Save and exit.

Test syntax with nginx -t.

Reload Nginx with nginx -s reload.

# nginx.conf or specific server config
http {
    # other config...
    server_tokens off;
    server {
        listen 80;
        server_name example.com;
        # server_tokens can also be set here
        location / {
            root /var/www/html;
        }
    }
}

Restrict Access to Sensitive Directories

Prevent external users from accessing .htaccess or .git directories using deny all; in location blocks.

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    location ~ \.git {
        deny all;
    }
    location ~ \.ht {
        deny all;
    }
}

When accessed, Nginx returns 403 Forbidden.

Custom error page example:

# custom 403 page configuration
error_page 403 /custom_403.html;
location = /custom_403.html {
    allow all;
    root /usr/share/nginx/html;
}

Configure Custom Error Pages

Set custom 404 and 5xx pages to improve UX and security.

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.html index.htm;
    server_tokens off;
    error_page 404 /custom_404.html;
    error_page 500 502 503 504 /custom_50x.html;
    location = /custom_404.html {
        root /usr/share/nginx/html;
        internal;
    }
    location = /custom_50x.html {
        root /usr/share/nginx/html;
        internal;
    }
}

Enable HTTPS

Obtain SSL certificate, generate CSR, install, and configure Nginx for HTTPS.

openssl req -new -newkey rsa:2048 -nodes -out yourdomain.csr -keyout yourdomain.key
# after obtaining certificate files:
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;
    ssl_certificate /path/to/yourdomain.crt;
    ssl_certificate_key /path/to/yourdomain.key;
    ssl_trusted_certificate /path/to/intermediate.crt;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    location / {
        root /var/www/html;
        index index.html index.htm;
    }
}

Apply Content Security Policy (CSP)

Add CSP header to mitigate XSS and data injection.

add_header Content-Security-Policy "default-src 'self'; img-src *; script-src 'self' 'unsafe-inline' 'unsafe-eval'";

Set Correct File Permissions

Use chmod 644 for files, 755 for directories, and set appropriate ownership.

# Set file permissions
chmod 644 /var/www/html/index.html
chmod 644 /etc/nginx/nginx.conf
chmod 644 /var/log/nginx/access.log
# Set directory permissions
chmod 755 /var/www/html/
# Change ownership
chown nginx:nginx /var/www/html/index.html
chown root:nginx /etc/nginx/nginx.conf
chown nginx:nginx /var/log/nginx/access.log

Configure Security Headers

# Clickjacking protection
add_header X-Frame-Options SAMEORIGIN;
add_header Content-Security-Policy "frame-ancestors 'self'";
# XSS protection
add_header X-XSS-Protection "1; mode=block";
# MIME sniffing protection
add_header X-Content-Type-Options nosniff;
# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Additional CSP
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'self'; upgrade-insecure-requests";

Limit Connections

Use ngx_http_limit_conn_module and ngx_http_limit_req_module to limit concurrent connections and request rate.

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    server {
        location / {
            limit_conn addr 10;
        }
    }
}
http {
    limit_req_zone $binary_remote_addr zone=req_zone:10m rate=20r/s;
    server {
        location / {
            limit_req zone=req_zone burst=5 nodelay;
        }
    }
}

Configure IP Whitelist

server {
    listen 80;
    server_name yourdomain.com;
    location /admin/ {
        allow 192.168.1.1;
        deny all;
        proxy_pass http://backend;
    }
}

Optimize SSL Configuration

server {
    listen 443 ssl http2;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 10s;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    ssl_session_tickets on;
}

File Upload Security

Limit upload size and prevent execution of scripts in upload directory.

http {
    client_max_body_size 10m;
    server {
        listen 80;
        server_name example.com;
        location /upload {
            proxy_pass http://backend_server;
            client_max_body_size 10m;
        }
        location /uploads {
            alias /path/to/uploads;
            location ~* \.(php|pl|py|jsp|asp|sh|cgi)$ {
                deny all;
            }
        }
    }
}

Prevent Common Attacks

Mitigate DDoS

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=30r/m;
    server {
        location /login.html {
            limit_req zone=one;
        }
        limit_conn_zone $binary_remote_addr zone=addr:10m;
        location /shopping/ {
            limit_conn addr 10;
        }
        client_body_timeout 5s;
        client_header_timeout 5s;
    }
}

SQL Injection Filter

if ($request_uri ~* [;'<>&]) { return 444; }
if ($args ~* [;'<>&]) { return 444; }
location ~* /(admin|backup|config|db|src)/ { deny all; }

XSS Protection

add_header X-XSS-Protection "1; mode=block";

Clickjacking Defense

add_header X-Frame-Options SAMEORIGIN;

Directory Traversal Prevention

location / { autoindex off; root /var/www/html; }
location ~ /\.\./ { deny all; }
location ~ /static/ { alias /var/www/static_files/; autoindex off; }
location /admin/ { allow 192.168.1.100; deny all; }

Logging Security

Define detailed log format and set appropriate error log level.

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" $msec "$connection" "$connection_requests" "$upstream_addr" "$upstream_response_time" "$request_time" "$gzip_ratio"';
    access_log /var/log/nginx/access.log main buffer=32k flush=1m;
    error_log /var/log/nginx/error.log warn;
    server {
        listen 80;
        server_name example.com;
        location / {
            root /var/www/html;
            index index.html;
        }
        error_log /var/log/nginx/example.error.log error;
    }
}

Other Security Measures

Disable Script Execution in Uploads

server {
    listen 80;
    server_name example.com;
    location /uploads/ {
        location ~* \ .php$ { deny all; }
    }
}

Conclusion

By disabling unnecessary HTTP methods, hiding version info, setting buffer limits, blocking unwanted crawlers, restricting IP access, controlling concurrency, using HTTPS with optimized SSL/TLS, and applying security headers such as CSP, administrators can significantly harden Nginx against known and unknown threats.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DevOpsHardening
Raymond Ops
Written by

Raymond Ops

Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.