How to Harden Nginx: Hide Version, Block Sensitive Files, and Stop Host Header Attacks
This guide walks you through securing an Nginx gateway by hiding its version, blocking access to sensitive files and directories, preventing Host header attacks, adding essential security response headers, and providing a complete configuration template with verification steps for enterprise‑grade protection.
Does your website show any of these high‑risk issues? Host header injection: attackers forge the Host header to steal password‑reset links; Sensitive file leakage: .git, .env, backup.zip can be downloaded directly; Directory traversal: accessing /static/../../../etc/passwd; Nginx version exposure: enables attackers to exploit known vulnerabilities.
Don’t rely on a “firewall‑only” approach—Nginx itself can serve as a powerful security gateway with just a few configuration lines.
Zero‑cost hardening
No extra software required
Meets compliance requirements (e.g., GB/T 22239‑2019)
🛡️ Step 1: Hide Identity – Disable Nginx Version Header
Attackers first probe your service version. Exposed version = a direct path to known exploits.
server_tokens off; ✅ Result:
Original header: Server: nginx/1.24.0
After hardening: Server: nginx🚫 Step 2: Block Entry – Deny Access to Sensitive Files and Directories
Many data leaks occur because a .env or .git file is publicly downloadable. Add the following global protection (recommended in the http block):
# Deny hidden files (starting with .)
location ~ /\.{
deny all;
return 404;
}
# Deny specific sensitive files
location ~* \.(env|git|svn|htaccess|htpasswd|bak|log|sql|zip|tar\.gz)$ {
deny all;
return 403;
}
# Deny backup files
location ~* \.bak$ {
deny all;
return 403;
}
# Deny directory traversal (explicit hardening)
location ~ \./\./ {
deny all;
return 403;
}
# Explanation:
~ = regex match
~* = case‑insensitive regex
deny all = immediately reject the request without returning file content🌐 Step 3: Lock Identity – Prevent Host Header Attacks (Critical!)
A Host header attack occurs when Nginx is configured with a permissive server_name (e.g., server_name _; or no default reject). An attacker can send:
GET /password-reset HTTP/1.1
Host: attacker.comIf backend code uses the Host header to generate reset links (e.g., https://attacker.com/reset?token=xxx), the attacker can steal credentials.
Security Configuration Plan
Set a default‑reject server block (place it first):
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _ "";
return 403;
}Specify allowed domain names in your business server block:
server {
listen 80;
server_name www.yourdomain.com yourdomain.com;
# Your business configuration...
}(Optional) Validate Host header at the application layer for defense‑in‑depth.
🛠️ Step 4: Add Security Response Headers (Browser Hardening)
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https:" always; Explanation:
X-Frame-Options: DENY – prevents clickjacking
X-Content-Type-Options: nosniff – prevents MIME sniffing
always – ensures error pages also carry these headers🧪 Verify Effectiveness
1. Check version hiding
curl -I http://your-domain.com→ Ensure the Server field does not contain a version number.
2. Test sensitive file access
curl -I http://your-domain.com/.env→ Should return 403 or 404, not 200.
3. Test Host header attack
curl -H "Host: evil.com" http://your-server-ip/→ Should return 403, not the business page.
📋 Complete Security Configuration Template (Save for Reference)
# /etc/nginx/nginx.conf or site config
# Hide version
server_tokens off;
# Default reject server (place at the top!)
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _ "";
return 403;
}
# Business server
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Security response headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https:" always;
# Sensitive file protection (can be placed in http block globally)
location ~ /\.{
deny all;
return 404;
}
location ~* \.(env|git|bak|log|sql|zip)$ {
deny all;
return 403;
}
# Your business locations
location / {
proxy_pass http://backend;
}
}🌟 Summary: Four Core Principles of a Secure Gateway
Minimal Exposure – hide version, disable unused modules
Least Privilege – allow only legitimate domains, reject unknown Hosts
Defense in Depth – Nginx + application‑layer validation
Proactive Protection – add security headers, block sensitive paths
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.
Xiao Liu Lab
An operations lab passionate about server tinkering 🔬 Sharing automation scripts, high-availability architecture, alert optimization, and incident reviews. Using technology to reduce overtime and experience to avoid major pitfalls. Follow me for easier, more reliable operations!
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.
