How to secure Nginx on Linux
How to Secure Nginx on Linux
Nginx is one of the world's most popular web servers, powering millions of websites globally. However, a default Nginx installation leaves your server vulnerable to various security threats. This comprehensive guide will walk you through essential security measures to harden your Nginx server on Linux, from basic configurations to advanced security implementations.
Table of Contents
1. [Prerequisites and Requirements](#prerequisites-and-requirements)
2. [Basic Nginx Security Configuration](#basic-nginx-security-configuration)
3. [SSL/TLS Configuration](#ssltls-configuration)
4. [Security Headers Implementation](#security-headers-implementation)
5. [Access Control and Authentication](#access-control-and-authentication)
6. [Rate Limiting and DDoS Protection](#rate-limiting-and-ddos-protection)
7. [Server Information Hiding](#server-information-hiding)
8. [File and Directory Security](#file-and-directory-security)
9. [Firewall Configuration](#firewall-configuration)
10. [Monitoring and Logging](#monitoring-and-logging)
11. [Advanced Security Measures](#advanced-security-measures)
12. [Common Issues and Troubleshooting](#common-issues-and-troubleshooting)
13. [Best Practices](#best-practices)
14. [Conclusion](#conclusion)
Prerequisites and Requirements
Before securing your Nginx server, ensure you have:
- A Linux server (Ubuntu 20.04/22.04, CentOS 7/8, or similar)
- Nginx installed and running
- Root or sudo access to the server
- Basic understanding of Linux command line
- Domain name pointing to your server (for SSL certificates)
- Text editor (nano, vim, or similar)
Checking Your Current Setup
First, verify your Nginx installation:
```bash
Check Nginx version
nginx -v
Check Nginx status
sudo systemctl status nginx
Test Nginx configuration
sudo nginx -t
```
Basic Nginx Security Configuration
1. Update Nginx to Latest Version
Always run the latest stable version of Nginx to ensure you have the most recent security patches:
```bash
Ubuntu/Debian
sudo apt update && sudo apt upgrade nginx
CentOS/RHEL
sudo yum update nginx
or for newer versions
sudo dnf update nginx
```
2. Configure Nginx User
Ensure Nginx runs under a non-privileged user account:
```bash
Edit main Nginx configuration
sudo nano /etc/nginx/nginx.conf
```
Add or modify the user directive at the top:
```nginx
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
```
3. Set Proper File Permissions
Configure appropriate permissions for Nginx files:
```bash
Set ownership for Nginx directories
sudo chown -R nginx:nginx /var/log/nginx/
sudo chown -R nginx:nginx /var/cache/nginx/
sudo chown -R nginx:nginx /etc/nginx/
Set proper permissions
sudo chmod 644 /etc/nginx/nginx.conf
sudo chmod -R 644 /etc/nginx/conf.d/
sudo chmod -R 644 /etc/nginx/sites-available/
sudo chmod 755 /var/log/nginx/
```
SSL/TLS Configuration
1. Obtain SSL Certificates
Using Let's Encrypt (Recommended)
Install Certbot for free SSL certificates:
```bash
Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx
CentOS/RHEL
sudo yum install certbot python3-certbot-nginx
```
Obtain certificates:
```bash
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
```
Manual Certificate Installation
If using purchased certificates, create the SSL directory:
```bash
sudo mkdir -p /etc/nginx/ssl
sudo chmod 700 /etc/nginx/ssl
```
2. Configure Strong SSL/TLS Settings
Create a dedicated SSL configuration file:
```bash
sudo nano /etc/nginx/conf.d/ssl.conf
```
Add the following secure SSL configuration:
```nginx
SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
Generate DH parameters
Run: openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
```
Generate Diffie-Hellman parameters:
```bash
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
```
3. Configure Virtual Host with SSL
Create or modify your site configuration:
```bash
sudo nano /etc/nginx/sites-available/your-site
```
Example secure virtual host configuration:
```nginx
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
root /var/www/html;
index index.html index.php;
# Security headers (detailed in next section)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
try_files $uri $uri/ =404;
}
}
```
Security Headers Implementation
Security headers protect against various attacks. Add these to your server block:
```nginx
Prevent clickjacking attacks
add_header X-Frame-Options "SAMEORIGIN" always;
Enable XSS filtering
add_header X-XSS-Protection "1; mode=block" always;
Prevent MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;
Control referrer information
add_header Referrer-Policy "no-referrer-when-downgrade" always;
Content Security Policy
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
HTTP Strict Transport Security
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Feature Policy (Permissions Policy)
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
```
Advanced CSP Configuration
For enhanced security, implement a stricter Content Security Policy:
```nginx
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self';
media-src 'self';
object-src 'none';
child-src 'self';
frame-ancestors 'none';
form-action 'self';
base-uri 'self';
" always;
```
Access Control and Authentication
1. HTTP Basic Authentication
Create password file:
```bash
Install htpasswd utility
sudo apt install apache2-utils # Ubuntu/Debian
sudo yum install httpd-tools # CentOS/RHEL
Create password file
sudo htpasswd -c /etc/nginx/.htpasswd username
```
Configure authentication in your site:
```nginx
location /admin {
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files $uri $uri/ =404;
}
```
2. IP-based Access Control
Restrict access by IP address:
```nginx
location /admin {
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
try_files $uri $uri/ =404;
}
```
3. Geographic Blocking
Using GeoIP module (if installed):
```nginx
In http block
geoip_country /usr/share/GeoIP/GeoIP.dat;
In server block
location / {
if ($geoip_country_code ~ (CN|RU|KP)) {
return 403;
}
try_files $uri $uri/ =404;
}
```
Rate Limiting and DDoS Protection
1. Configure Rate Limiting
Add to your `nginx.conf` http block:
```nginx
Define rate limiting zones
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/m;
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/m;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
Connection limiting
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
```
Apply limits in server blocks:
```nginx
server {
# Limit connections per IP
limit_conn conn_limit_per_ip 10;
location /login {
limit_req zone=login burst=5 nodelay;
# Your login page configuration
}
location /api/ {
limit_req zone=api burst=10 nodelay;
# Your API configuration
}
location / {
limit_req zone=general burst=20 nodelay;
try_files $uri $uri/ =404;
}
}
```
2. Configure Request Size Limits
Prevent large file uploads and DoS attacks:
```nginx
In http block
client_max_body_size 10M;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
```
3. Timeout Configuration
Set appropriate timeouts:
```nginx
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
```
Server Information Hiding
1. Hide Nginx Version
Add to your `nginx.conf` http block:
```nginx
server_tokens off;
```
2. Hide Server Header
For complete server header removal, you'll need to compile Nginx with the `headers-more` module or use it if available:
```nginx
If headers-more module is available
more_set_headers "Server: ";
```
3. Custom Error Pages
Create custom error pages to avoid revealing server information:
```nginx
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /404.html {
root /var/www/error;
internal;
}
location = /50x.html {
root /var/www/error;
internal;
}
```
File and Directory Security
1. Restrict Access to Sensitive Files
```nginx
Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
Deny access to backup files
location ~ ~$ {
deny all;
access_log off;
log_not_found off;
}
Deny access to configuration files
location ~* \.(conf|sql|sh|key|cert|pem)$ {
deny all;
access_log off;
log_not_found off;
}
Protect log files
location ~* \.(log)$ {
deny all;
access_log off;
log_not_found off;
}
```
2. Disable Directory Browsing
```nginx
autoindex off;
```
3. Secure File Uploads
If you allow file uploads:
```nginx
location /uploads {
location ~ \.(php|php5|phtml|pl|py|jsp|asp|sh|cgi)$ {
deny all;
}
}
```
Firewall Configuration
1. Configure UFW (Ubuntu)
```bash
Enable UFW
sudo ufw enable
Allow SSH
sudo ufw allow ssh
Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Deny all other incoming traffic
sudo ufw default deny incoming
sudo ufw default allow outgoing
Check status
sudo ufw status verbose
```
2. Configure firewalld (CentOS/RHEL)
```bash
Start and enable firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalld
Allow HTTP and HTTPS
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
Reload configuration
sudo firewall-cmd --reload
Check status
sudo firewall-cmd --list-all
```
3. Configure iptables (Advanced)
Create a basic iptables configuration:
```bash
#!/bin/bash
Basic iptables configuration for web server
Flush existing rules
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
Set default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
Allow loopback traffic
iptables -A INPUT -i lo -j ACCEPT
Allow established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
Allow SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Allow HTTP and HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
Save rules (Ubuntu/Debian)
iptables-save > /etc/iptables/rules.v4
```
Monitoring and Logging
1. Configure Comprehensive Logging
Modify your Nginx configuration for detailed logging:
```nginx
Custom log format
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time '
'$pipe $upstream_cache_status';
Apply to server blocks
access_log /var/log/nginx/access.log detailed;
error_log /var/log/nginx/error.log warn;
```
2. Log Rotation
Configure logrotate for Nginx logs:
```bash
sudo nano /etc/logrotate.d/nginx
```
```
/var/log/nginx/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 644 nginx nginx
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
```
3. Monitoring with Fail2ban
Install and configure Fail2ban:
```bash
sudo apt install fail2ban # Ubuntu/Debian
sudo yum install fail2ban # CentOS/RHEL
```
Create Nginx jail configuration:
```bash
sudo nano /etc/fail2ban/jail.local
```
```ini
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 10
bantime = 600
[nginx-botsearch]
enabled = true
filter = nginx-botsearch
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 86400
```
Advanced Security Measures
1. ModSecurity Web Application Firewall
Install ModSecurity for Nginx:
```bash
Ubuntu/Debian
sudo apt install libnginx-mod-security
Add to nginx.conf
load_module modules/ngx_http_modsecurity_module.so;
```
Configure ModSecurity:
```nginx
In server block
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
```
2. Implement HSTS Preloading
Add your domain to HSTS preload list:
```nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
```
3. Certificate Transparency Monitoring
Monitor your certificates using Certificate Transparency logs and set up alerts for unauthorized certificates.
4. Security Scanning and Testing
Regularly test your configuration:
```bash
SSL Labs test
Visit: https://www.ssllabs.com/ssltest/
Mozilla Observatory
Visit: https://observatory.mozilla.org/
Test with testssl.sh
git clone https://github.com/drwetter/testssl.sh.git
cd testssl.sh
./testssl.sh yourdomain.com
```
Common Issues and Troubleshooting
1. SSL Certificate Issues
Problem: SSL certificate not loading
Solution:
```bash
Check certificate validity
sudo openssl x509 -in /path/to/certificate.crt -text -noout
Verify certificate chain
sudo nginx -t
Check certificate permissions
sudo ls -la /etc/letsencrypt/live/yourdomain.com/
```
2. Rate Limiting Too Aggressive
Problem: Legitimate users being blocked
Solution:
```nginx
Adjust burst values and add nodelay
limit_req zone=general burst=50 nodelay;
Use different zones for different content types
limit_req zone=static burst=100 nodelay;
```
3. Security Headers Breaking Functionality
Problem: CSP blocking legitimate resources
Solution:
```nginx
Start with report-only mode
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report" always;
Gradually tighten policy based on reports
```
4. Performance Impact from Security Measures
Problem: Security configurations slowing down site
Solution:
```nginx
Optimize SSL session caching
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
Use HTTP/2
listen 443 ssl http2;
Enable gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
```
5. Firewall Blocking Legitimate Traffic
Problem: Users unable to access site after firewall configuration
Solution:
```bash
Check firewall status
sudo ufw status verbose
sudo firewall-cmd --list-all
Temporarily disable to test
sudo ufw disable
sudo systemctl stop firewalld
Check logs for blocked connections
sudo tail -f /var/log/ufw.log
sudo journalctl -u firewalld -f
```
Best Practices
1. Regular Security Maintenance
- Update regularly: Keep Nginx, SSL certificates, and system packages updated
- Monitor logs: Regularly review access and error logs for suspicious activity
- Backup configurations: Maintain backups of your Nginx configurations
- Test changes: Always test configuration changes in a staging environment first
2. Security Configuration Management
```bash
Create configuration backup script
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
tar -czf /backup/nginx_config_$DATE.tar.gz /etc/nginx/
```
3. Performance vs Security Balance
- Use appropriate cache settings for SSL sessions
- Implement security headers efficiently
- Monitor server performance after implementing security measures
- Use CDN services for additional DDoS protection
4. Documentation and Incident Response
- Document all security configurations and changes
- Create incident response procedures
- Set up monitoring and alerting for security events
- Regular security audits and penetration testing
5. Compliance Considerations
- Ensure configurations meet relevant compliance requirements (PCI DSS, GDPR, etc.)
- Implement appropriate logging for audit trails
- Regular security assessments and vulnerability scanning
Conclusion
Securing Nginx on Linux requires a comprehensive approach involving multiple layers of protection. This guide has covered essential security measures including SSL/TLS configuration, security headers, access controls, rate limiting, and monitoring.
Key takeaways for maintaining a secure Nginx installation:
1. Keep everything updated: Regular updates are your first line of defense
2. Implement defense in depth: Use multiple security layers rather than relying on single measures
3. Monitor continuously: Set up proper logging and monitoring to detect issues early
4. Test thoroughly: Always test security configurations before deploying to production
5. Stay informed: Keep up with the latest security threats and Nginx security best practices
Remember that security is an ongoing process, not a one-time setup. Regularly review and update your security configurations, monitor for new threats, and adapt your security posture as your application and threat landscape evolve.
For production environments, consider implementing additional security measures such as Web Application Firewalls (WAF), DDoS protection services, and regular security audits. The investment in proper security configuration will protect your applications, data, and users from various cyber threats.
By following this comprehensive guide, you'll have a significantly more secure Nginx installation that can withstand common attacks while maintaining good performance and usability for legitimate users.