How to Harden Nginx: Essential Security Practices for Safer Web Servers
This guide provides a comprehensive, step‑by‑step hardening roadmap for Nginx, covering version hiding, directory protection, HTTPS enablement, custom error pages, CSP, file permissions, security headers, connection limits, IP whitelisting, SSL optimization, secure file uploads, common attack mitigations, logging best practices, and additional hardening measures to protect web services from a wide range of threats.
Introduction
In the digital age, web security is critical, and Nginx, as a popular high‑performance web server, offers strong security features that must be properly configured to defend against threats. This guide presents a detailed hardening roadmap for system administrators and developers.
Hide Version Information
Nginx exposes its version in the HTTP response header by default. Adding server_tokens off; in the http, server or location block disables this disclosure.
Open the Nginx configuration file (usually /etc/nginx/nginx.conf or /usr/local/nginx/conf/nginx.conf).
Edit the file and add server_tokens off; inside the http block or each server block.
Save and exit the editor.
Test the configuration with nginx -t.
Reload Nginx using nginx -s reload.
# nginx.conf or a specific server file
http {
# other configuration...
server_tokens off;
server {
listen 80;
server_name example.com;
location / {
root /var/www/html;
index index.html index.htm;
}
}
}Restrict Access to Sensitive Directories
Prevent external users from accessing files such as .htaccess or the .git directory by using location blocks with deny all;.
server {
listen 80;
server_name example.com;
root /var/www/html;
location ~ \.git {
deny all;
}
location ~ \.ht {
deny all;
}
}A custom 403 error page can be defined with error_page 403 /custom_403.html; and served from /usr/share/nginx/html.
server {
listen 80;
server_name example.com;
root /var/www/html;
error_page 403 /custom_403.html;
location ~ \.git { deny all; }
location ~ \.ht { deny all; }
location = /custom_403.html {
allow all;
root /usr/share/nginx/html;
}
}Configure Custom Error Pages
Define custom pages for 404, 500, etc., to improve user experience and avoid leaking server details.
server {
listen 80;
server_name example.com;
root /var/www/html;
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 an SSL certificate, generate a CSR, and configure Nginx to redirect HTTP to HTTPS and serve secure traffic.
# Generate CSR
openssl req -new -newkey rsa:2048 -nodes -out yourdomain.csr -keyout yourdomain.key
# Nginx HTTPS configuration
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 a CSP header to restrict resource loading and mitigate XSS and data‑injection attacks.
server {
listen 80;
server_name example.com;
add_header Content-Security-Policy "default-src 'self'; img-src *; script-src 'self' 'unsafe-inline' 'unsafe-eval'";
}For stricter policies, specify trusted script, style, image, and API sources, and disable objects and frames.
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trustedscripts.example.com; style-src 'self' https://trustedstyles.example.com; img-src 'self' data: https://trustedimages.example.com; connect-src 'self' https://api.example.com; object-src 'none'; frame-src 'none';";Monitor violations with report-uri to a designated endpoint.
add_header Content-Security-Policy "default-src 'self'; report-uri /csp-report-endpoint";Set Correct File Permissions
Use chmod 644 for files and chmod 755 for directories. Ensure the Nginx process runs under a non‑root user (e.g., nginx or www-data).
# Set file permissions
sudo chmod 644 /var/www/html/index.html /etc/nginx/nginx.conf /var/log/nginx/access.log
# Set directory permissions
sudo chmod 755 /var/www/html/
# Change ownership
sudo chown nginx:nginx /var/www/html/index.html
sudo chown root:nginx /etc/nginx/nginx.conf
sudo chown nginx:nginx /var/log/nginx/access.logConfigure 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‑type 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 restrict concurrent connections and request rates per IP.
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location / { limit_conn addr 10; }
}
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;
}Secure File Uploads
Limit upload size with client_max_body_size and prevent execution of uploaded scripts.
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
DDoS Mitigation
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 Mitigation
location / {
if ($request_uri ~* [;'<>' ]) { return 444; }
if ($args ~* [;'<>' ]) { return 444; }
location ~* /(admin|backup|config|db|src)/ { deny all; }
}XSS Mitigation
add_header X-XSS-Protection "1; mode=block";Clickjacking Protection
add_header X-Frame-Options SAMEORIGIN;Directory Traversal Protection
# Disable autoindex
server {
listen 80;
server_name example.com;
location / { autoindex off; root /var/www/html; }
}
# Block "../" patterns
location ~ /\.\./ { deny all; }
# Correct alias usage
location /static/ { alias /var/www/static_files/; autoindex off; }Log Security
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
Disallow Script Execution
server {
listen 80;
server_name example.com;
location /uploads/ {
location ~* \.php$ { deny all; }
}
}Configure Timeouts
http {
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;
keepalive_timeout 60s;
client_max_body_size 1M;
limit_rate 100k;
server {
listen 80;
server_name example.com;
location / { }
}
}Conclusion
By disabling unnecessary HTTP methods, hiding version information, limiting buffers, blocking unwanted crawlers, restricting IP access, controlling concurrency and rate, tuning timeouts, and adopting HTTPS with optimized SSL/TLS settings, Nginx security can be significantly strengthened. Proper CSP, regular log audits, minimal‑privilege operation, and keeping Nginx up‑to‑date complete a robust defense against both known and emerging threats.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
