Nginx Security Best Practices: Protect Your Web Server
Protect your Nginx web server with essential security best practices. This guide covers securing SSL/TLS connections, implementing effective rate limiting to prevent abuse, mitigating common web attacks like XSS and SQL injection, and the critical importance of keeping Nginx updated. Learn actionable steps and configuration examples to enhance your server's security and safeguard your online presence.
Nginx Security Best Practices: Protect Your Web Server
Your Nginx server is often the first public service users and attackers can reach. These Nginx security best practices focus on the controls Nginx can actually enforce: TLS, request limits, headers, access rules, safer defaults, and regular updates.
Nginx cannot fix vulnerable application code by itself. Treat it as one layer in front of your app, database, authentication system, and host firewall.
Secure Connections with TLS
TLS encrypts traffic between the browser and your server. Use a trusted certificate, redirect HTTP to HTTPS, and disable obsolete protocol versions.
Get a Certificate
Let's Encrypt is common for public websites, but any trusted certificate authority can work. On many Linux servers, Certbot stores files under /etc/letsencrypt/live/your_domain.com/.
Configure HTTPS
Edit the server block for your domain. The path is often /etc/nginx/sites-available/ on Debian/Ubuntu or /etc/nginx/conf.d/ on RHEL-style systems.
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name your_domain.com www.your_domain.com;
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
# Include your shared TLS settings
include /etc/nginx/snippets/ssl-params.conf;
# ... other configurations (root, location blocks, etc.)
}
server {
listen 80;
listen [::]:80;
server_name your_domain.com www.your_domain.com;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
}
Use Conservative TLS Parameters
You can keep shared TLS settings in a snippet such as /etc/nginx/snippets/ssl-params.conf.
# /etc/nginx/snippets/ssl-params.conf
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:DHE-RSA-AES256-GCM-SHA384;
# Enable HSTS (HTTP Strict Transport Security)
# Add includeSubDomains if applicable
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Enable OCSP Stapling for faster certificate checks
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Diffie-Hellman parameters (generate a strong one if needed)
# ssl_dhparam /etc/nginx/ssl/dhparams.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
Only add includeSubDomains or preload to HSTS after you know every subdomain supports HTTPS. A bad HSTS rollout can lock users out of older subdomains.
Implement Rate Limiting
Rate limiting helps slow brute-force attempts, scraping, and accidental request floods. It is not a full DDoS solution, but it gives your app more breathing room.
Basic Rate Limit Example
limit_req_zone defines shared state, and limit_req applies the limit to a location.
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=5r/s;
server {
# ...
location /login {
limit_req zone=mylimit burst=10 nodelay;
# ... your login handler configuration
}
location / {
limit_req zone=mylimit burst=20 nodelay;
# ... your main site configuration
}
}
}
In this example:
$binary_remote_addr keys the limit by client IP address. burst=10 allows a short burst above the average rate, and nodelay rejects excessive requests instead of delaying them.
Be careful behind a load balancer or CDN. If Nginx only sees the proxy's IP address, rate limiting by $binary_remote_addr can punish all users as one client. Configure trusted real IP handling before relying on per-client limits.
Reduce Common Attack Surface
Nginx can reduce exposure, but your application still needs input validation, output encoding, parameterized SQL, authentication checks, and dependency patching.
Add Security Headers
Security headers can reduce browser-side risk:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
A Content Security Policy can help with XSS, but it must match your app's scripts, styles, images, and third-party services. Start in report-only mode before enforcing it on a production site.
Block Obvious Scanners Carefully
Simple pattern blocks can stop noisy scanners, but they are not a substitute for a WAF. Keep them narrow so you do not block legitimate users.
http {
# Define a map for blocking bad bots/scanners
map $http_user_agent $bad_bot {
default 0;
"~*malicious_bot_pattern" 1;
"~*another_suspicious_agent" 1;
}
server {
# ...
if ($bad_bot) {
return 403;
}
# ...
}
}
Restrict Sensitive Paths
Use try_files, keep autoindex off unless you need directory listings, and deny access to hidden files that should never be served.
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ =404;
autoindex off; # Disable directory listing
}
# Example of restricting access to sensitive files
location ~ /\.ht {
deny all;
}
Hide the Nginx Version
server_tokens off removes the Nginx version from generated error pages and reduces detail in the Server response header.
http {
server_tokens off;
# ...
}
Limit HTTP Methods Where Appropriate
If an endpoint only accepts GET and POST, reject other methods there. Do this per location so you do not break CORS preflight requests or API endpoints that legitimately use PUT, PATCH, or DELETE.
location /api/ {
# Allow only GET and POST
if ($request_method !~ ^(GET|POST)$) {
return 405;
}
# ...
}
Keep Nginx Updated
Security fixes often arrive through your Linux distribution's package repository. Keep Nginx and OpenSSL-related packages patched, and test reloads after updates.
For Debian/Ubuntu:
sudo apt update
sudo apt upgrade nginx
For CentOS/RHEL:
sudo dnf update nginx
Use yum update nginx on older RHEL/CentOS releases that still use yum.
Add Host-Level Protection
Nginx security also depends on the host around it:
- Allow only necessary ports in
ufw,firewalld, cloud security groups, or network ACLs. - Monitor
/var/log/nginx/access.logand/var/log/nginx/error.logfor spikes, repeated 401/403 responses, and suspicious paths. - Use Fail2ban or a similar tool when log patterns can identify abusive clients.
- Run
sudo nginx -tbefore each reload so a security change does not take the site offline.
Takeaway
Start with HTTPS, updates, restricted ports, safe headers, and rate limits on sensitive paths such as /login. Then review your logs regularly. Most Nginx security wins come from steady maintenance and clear configuration, not from one large hardening file copied from another site.