Essential Best Practices for Securing and Optimizing Nginx on Ubuntu

Essential Best Practices for Securing and Optimizing Nginx on Ubuntu

What is Nginx?

Nginx: Nginx is a high-performance web server and reverse proxy known for its scalability and efficient handling of concurrent connections.

Nginx vs Apache2

Nginx is designed for high concurrency and low memory usage, making it ideal for static content and reverse proxying, while Apache2 is highly customizable with a rich feature set and strong support for dynamic content.

Nginx uses an event-driven, asynchronous architecture that allows it to handle a large number of concurrent connections with low resource usage, making it ideal for serving static content and acting as a reverse proxy. Apache2, on the other hand, employs a process-driven model where each connection requires a separate process or thread, offering extensive customization and a wide range of modules for dynamic content but often requiring more memory and resources under heavy loads.

Best Practices for Nginx Configuration

  1. Keep Nginx Up-to-Date

    • Ensure Nginx is always updated to the latest stable version to benefit from security patches and performance improvements.

    • Use the official Nginx repository for the latest updates

        sudo apt update
        sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
        curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
          | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
        echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
          | sudo tee /etc/apt/sources.list.d/nginx.list
        sudo apt update
        sudo apt install nginx
      
  2. Use Separate Configuration Files

    • Divide your configuration into multiple files and include them in the main nginx.conf file. This makes management easier.

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
      
  3. Enable Gzip Compression

    • Reduce the size of the data sent to clients

        gzip on;
        gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
        gzip_proxied any;
        gzip_comp_level 5;
      
  4. Security Best Practices

    • Disable server tokens to hide Nginx version

        server_tokens off;
      
    • Use secure headers

        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-Content-Type-Options "nosniff";
        add_header X-XSS-Protection "1; mode=block";
      
    • Restrict access to sensitive files

        location ~ /\.(ht|git|svn) {
            deny all;
        }
      
  5. Optimize SSL/TLS Configuration

    • Use strong ciphers and enable HTTP/2

        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        http2;
      
  6. Logging and Monitoring

    • Use separate log files for each site and log error levels appropriately

        access_log /var/log/nginx/project-name/access.log;
        error_log /var/log/nginx/project-name/error.log warn;
      
  7. Caching

    • Use caching to improve performance

        proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m inactive=60m;
        proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
        proxy_cache_use_stale error timeout updating invalid_header http_500 http_502 http_503 http_504;
      
  8. Rate Limiting and Connection Limits

    • Prevent abuse by limiting the number of requests from a single IP

        limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
        limit_req zone=one burst=10 nodelay;
      
  9. Static Content Delivery

    • Serve static files directly to reduce load on the backend

        location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
            expires 1d;
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        }
      
  10. Check Configuration Syntax

    • Before applying the changes, check for syntax errors

        sudo nginx -t
      
  11. Reload Nginx

    • Apply the changes by reloading Nginx

        sudo systemctl reload nginx
      
  12. Monitor Nginx

    • Ensure Nginx is running smoothly by checking its status

        sudo systemctl status nginx
      
  13. Use the$upstream_cache_status Variable for Better Caching Insights

    • You can log cache statuses in your access log for better monitoring.

        log_format cache '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$upstream_cache_status"';
        access_log /var/log/nginx/access.log cache;
      
  14. Dynamic Module Loading

    • Nginx supports dynamically loadable modules. This allows you to load specific modules only when needed, reducing the overall memory footprint.

        load_module modules/ngx_http_image_filter_module.so;
        load_module modules/ngx_http_geoip_module.so;
      
  15. Client-Specific Logging

    • Use conditional logging to log only specific types of requests, which can reduce log volume and focus on critical data.

        map $status $loggable {
            ~^[23]  0;
            default 1;
        }
        access_log /var/log/nginx/access.log combined if=$loggable;
      
  16. Leverage$request_id for Tracing Requests

    • Generate a unique request ID to trace requests across multiple logs and services.

        http {
            log_format main '$request_id $remote_addr - $remote_user [$time_local] '
                            '"$request" $status $body_bytes_sent '
                            '"$http_referer" "$http_user_agent"';
            map $request_id $loggable_request_id {
                default $request_id;
            }
            server {
                set $request_id $request_id;
                access_log /var/log/nginx/access.log main;
            }
        }
      
  17. Cache Configuration Best Practices

    • Avoid unnecessary caching of dynamic content by fine-tuning your cache configuration:

        location / {
            proxy_cache my_cache;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            proxy_cache_bypass $http_cache_control;
            proxy_no_cache $http_cache_control;
        }
      
  18. Use$http_host for Multi-Domain SSL Certificates

    • Simplify your SSL configuration for multiple domains by using a wildcard certificate and the $http_host variable

        server {
            listen 443 ssl;
            server_name *.example.com;
            ssl_certificate /etc/nginx/ssl/example.com.crt;
            ssl_certificate_key /etc/nginx/ssl/example.com.key;
      
            location / {
                proxy_pass https://$http_host$request_uri;
            }
        }
      
  19. Rate Limiting by URL Path

    • Apply rate limiting rules specifically to certain paths rather than globally.

        location /api/ {
            limit_req zone=api burst=5 nodelay;
        }
      
  20. Optimize Buffer Sizes

    • Adjust buffer sizes to improve performance for large requests or responses.

        client_body_buffer_size 16k;
        client_header_buffer_size 1k;
        large_client_header_buffers 4 4k;
      

Additional Tips

Regular Maintenance: Regularly clean up old logs, review access and error logs, and remove any unnecessary configuration files to keep your Nginx server running smoothly.

Did you find this article valuable?

Support Muhammad Usama by becoming a sponsor. Any amount is appreciated!