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.