Configure NGINX rate limiting and DDoS protection with advanced security rules

Intermediate 45 min Apr 02, 2026 373 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Learn to configure production-grade NGINX rate limiting and DDoS protection with multiple security zones, advanced rules, and real-time monitoring to protect your web applications from malicious traffic and resource exhaustion attacks.

Prerequisites

  • Root or sudo access
  • Basic NGINX knowledge
  • Understanding of HTTP protocols
  • Mail server for alerts (optional)

What this solves

Rate limiting and DDoS protection are critical for protecting web applications from traffic spikes, brute force attacks, and resource exhaustion. This tutorial configures NGINX with multiple rate limiting zones, adaptive security rules, and monitoring capabilities to automatically block malicious traffic while maintaining legitimate user access.

Step-by-step configuration

Update system packages and install NGINX

Start by updating your system and installing NGINX with required modules for advanced security features.

sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx nginx-module-njs geoip-database
sudo dnf update -y
sudo dnf install -y nginx nginx-mod-http-geoip GeoIP-devel

Enable NGINX security modules

Enable essential NGINX modules for enhanced security and rate limiting capabilities.

# Add at the top of nginx.conf, before events block
load_module modules/ngx_http_geoip_module.so;
load_module modules/ngx_http_realip_module.so;
load_module modules/ngx_http_limit_req_module.so;
load_module modules/ngx_http_limit_conn_module.so;

Configure rate limiting zones

Create multiple rate limiting zones for different types of traffic patterns and attack vectors.

# Rate limiting zones configuration
http {
    # Define rate limiting zones
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
    limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
    limit_req_zone $binary_remote_addr zone=search:10m rate=5r/s;
    limit_req_zone $binary_remote_addr zone=upload:10m rate=1r/m;
    
    # Connection limiting zones
    limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
    limit_conn_zone $server_name zone=conn_limit_per_server:10m;
    
    # Define real IP from trusted proxies
    set_real_ip_from 10.0.0.0/8;
    set_real_ip_from 172.16.0.0/12;
    set_real_ip_from 192.168.0.0/16;
    set_real_ip_from 203.0.113.0/24;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;
}

Create GeoIP blocking configuration

Configure geographic blocking to automatically deny traffic from high-risk countries or regions.

# GeoIP configuration for geographic blocking
http {
    geoip_country /usr/share/GeoIP/GeoIP.dat;
    
    # Map countries to blocking rules
    map $geoip_country_code $blocked_country {
        default 0;
        # Add country codes to block (examples)
        CN 1;  # China
        RU 1;  # Russia
        # Add more as needed
    }
    
    # Map for allowed countries (whitelist approach)
    map $geoip_country_code $allowed_country {
        default 0;
        US 1;  # United States
        CA 1;  # Canada
        GB 1;  # United Kingdom
        DE 1;  # Germany
        FR 1;  # France
        # Add your allowed countries
    }
}

Configure advanced DDoS protection rules

Implement comprehensive DDoS protection with multiple layers of security checks and automatic blocking.

# Advanced DDoS protection configuration
http {
    # Request size limits
    client_body_buffer_size 1K;
    client_header_buffer_size 1k;
    client_max_body_size 1k;
    large_client_header_buffers 2 1k;
    
    # Timeout settings
    client_body_timeout 10;
    client_header_timeout 10;
    keepalive_timeout 5 5;
    send_timeout 10;
    
    # Buffer overflow protection
    server_tokens off;
    
    # Limit request methods
    map $request_method $not_allowed_method {
        default 1;
        GET 0;
        POST 0;
        HEAD 0;
    }
    
    # Block suspicious user agents
    map $http_user_agent $blocked_agent {
        default 0;
        ~*bot 1;
        ~*crawler 1;
        ~*spider 1;
        ~*scanner 1;
        "" 1;  # Empty user agent
        "-" 1;  # Dash user agent
    }
    
    # Block requests without proper referrer (for POST requests)
    map $request_method:$http_referer $suspicious_request {
        default 0;
        "POST:" 1;
        "POST:-" 1;
    }
}

Configure server block with security rules

Apply rate limiting and security rules to your server configuration with proper error handling.

server {
    listen 80;
    server_name example.com www.example.com;
    
    # Apply connection limits
    limit_conn conn_limit_per_ip 10;
    limit_conn conn_limit_per_server 100;
    
    # Block based on geographic location (if using blacklist)
    if ($blocked_country) {
        return 403 "Access denied from your location";
    }
    
    # Block suspicious user agents
    if ($blocked_agent) {
        return 403 "Blocked user agent";
    }
    
    # Block non-allowed HTTP methods
    if ($not_allowed_method) {
        return 405 "Method not allowed";
    }
    
    # Apply general rate limiting
    limit_req zone=general burst=20 nodelay;
    limit_req_status 429;
    
    # Custom error pages for rate limiting
    error_page 403 /403.html;
    error_page 429 /429.html;
    
    location = /403.html {
        root /var/www/html;
        internal;
    }
    
    location = /429.html {
        root /var/www/html;
        internal;
    }
    
    # Login endpoint with strict rate limiting
    location /login {
        limit_req zone=login burst=3 nodelay;
        # Your login handler
        try_files $uri $uri/ =404;
    }
    
    # API endpoints with moderate rate limiting
    location /api/ {
        limit_req zone=api burst=10 nodelay;
        # Your API handler
        try_files $uri $uri/ =404;
    }
    
    # Search functionality with burst protection
    location /search {
        limit_req zone=search burst=10;
        # Your search handler
        try_files $uri $uri/ =404;
    }
    
    # File upload with very strict limits
    location /upload {
        limit_req zone=upload burst=1 nodelay;
        client_max_body_size 10M;
        # Your upload handler
        try_files $uri $uri/ =404;
    }
    
    # Static files with relaxed limits
    location ~* \.(css|js|png|jpg|jpeg|gif|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }
    
    # Block access to sensitive files
    location ~* \.(htaccess|htpasswd|ini|conf|log)$ {
        deny all;
        return 404;
    }
    
    # Main location block
    location / {
        try_files $uri $uri/ =404;
    }
}

Create custom error pages

Design informative error pages for rate-limited and blocked users to improve user experience.

sudo mkdir -p /var/www/html
sudo chown -R www-data:www-data /var/www/html



    Rate Limited - Too Many Requests
    
    


    

429 - Too Many Requests

You have exceeded the rate limit. Please wait a moment and try again.

If you believe this is an error, please contact support.




    Access Denied
    
    


    

403 - Access Denied

Your request has been blocked by our security system.

Configure logging for security monitoring

Set up detailed logging to monitor rate limiting effectiveness and security events.

# Security-focused logging configuration
http {
    # Custom log format for security analysis
    log_format security_log '$remote_addr - $remote_user [$time_local] '
                           '"$request" $status $body_bytes_sent '
                           '"$http_referer" "$http_user_agent" '
                           '$request_time $upstream_response_time '
                           '$geoip_country_code $geoip_country_name '
                           'rate_limit: $limit_req_status';
    
    # Separate log for blocked requests
    log_format blocked_log '$remote_addr [$time_local] "$request" '
                          'blocked: $status reason: "$sent_http_content_type" '
                          'agent: "$http_user_agent" '
                          'country: $geoip_country_code';
    
    # Access log with security information
    access_log /var/log/nginx/access.log security_log;
    
    # Error log for debugging
    error_log /var/log/nginx/error.log warn;
}

Create monitoring script

Implement a monitoring script to track rate limiting statistics and generate alerts for unusual activity.

#!/bin/bash

NGINX Security Monitoring Script

Monitors rate limiting and generates alerts

LOG_FILE="/var/log/nginx/access.log" ALERT_EMAIL="admin@example.com" THRESHOLD_429=100 # Alert if more than 100 rate limit hits in 5 minutes THRESHOLD_403=50 # Alert if more than 50 blocked requests in 5 minutes

Check rate limiting hits in last 5 minutes

RATE_LIMIT_COUNT=$(grep "$(date -d '5 minutes ago' '+%d/%b/%Y:%H:%M')" "$LOG_FILE" | grep -c " 429 ") BLOCKED_COUNT=$(grep "$(date -d '5 minutes ago' '+%d/%b/%Y:%H:%M')" "$LOG_FILE" | grep -c " 403 ")

Generate alert if thresholds exceeded

if [ "$RATE_LIMIT_COUNT" -gt "$THRESHOLD_429" ]; then echo "ALERT: High rate limiting activity detected - $RATE_LIMIT_COUNT hits in 5 minutes" | \ mail -s "NGINX Security Alert - Rate Limiting" "$ALERT_EMAIL" fi if [ "$BLOCKED_COUNT" -gt "$THRESHOLD_403" ]; then echo "ALERT: High blocking activity detected - $BLOCKED_COUNT blocks in 5 minutes" | \ mail -s "NGINX Security Alert - Access Blocked" "$ALERT_EMAIL" fi

Generate daily security report

if [ "$(date +%H:%M)" = "09:00" ]; then echo "Daily NGINX Security Report - $(date)" > /tmp/security_report.txt echo "Rate Limited Requests (429): $(grep -c " 429 " "$LOG_FILE")" >> /tmp/security_report.txt echo "Blocked Requests (403): $(grep -c " 403 " "$LOG_FILE")" >> /tmp/security_report.txt echo "Top Blocked IPs:" >> /tmp/security_report.txt grep " 403 " "$LOG_FILE" | awk '{print $1}' | sort | uniq -c | sort -nr | head -10 >> /tmp/security_report.txt mail -s "Daily NGINX Security Report" "$ALERT_EMAIL" < /tmp/security_report.txt fi
sudo chmod +x /usr/local/bin/nginx-security-monitor.sh
sudo chown root:root /usr/local/bin/nginx-security-monitor.sh

Set up log rotation

Configure log rotation to manage disk space while retaining security logs for analysis.

/var/log/nginx/*.log {
    daily
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 0644 www-data adm
    sharedscripts
    prerotate
        if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
            run-parts /etc/logrotate.d/httpd-prerotate; \
        fi 
    endscript
    postrotate
        invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

Test configuration and start NGINX

Validate your NGINX configuration and start the service with rate limiting enabled.

sudo nginx -t
sudo systemctl enable --now nginx
sudo systemctl status nginx

Configure monitoring cron job

Set up automated monitoring to run the security monitoring script every 5 minutes.

sudo crontab -e

Add this line to run monitoring every 5 minutes:

/5    * /usr/local/bin/nginx-security-monitor.sh >/dev/null 2>&1

Verify your setup

Test your rate limiting configuration and verify that security rules are working properly.

# Check NGINX configuration
sudo nginx -t

Verify NGINX is running

sudo systemctl status nginx

Test rate limiting with curl

for i in {1..15}; do curl -I http://example.com; sleep 1; done

Check rate limiting logs

sudo tail -f /var/log/nginx/access.log | grep "429\|403"

Monitor real-time connections

watch 'ss -tuln | grep :80'

Check security zones status

sudo nginx -T | grep -A 10 "limit_req_zone"

Performance tuning

Optimize rate limiting performance for high-traffic environments by adjusting zone sizes and caching parameters.

# Performance optimization for rate limiting
http {
    # Increase worker processes for better performance
    worker_processes auto;
    worker_rlimit_nofile 65535;
    
    # Optimize event handling
    events {
        worker_connections 4096;
        use epoll;
        multi_accept on;
    }
    
    # Optimize rate limiting zones for high traffic
    limit_req_zone $binary_remote_addr zone=general_fast:50m rate=50r/s;
    limit_req_zone $binary_remote_addr zone=api_fast:50m rate=1000r/m;
    
    # Enable gzip compression to reduce bandwidth
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript;
    
    # Optimize TCP settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
}

Common issues

SymptomCauseFix
NGINX fails to startConfiguration syntax errorsudo nginx -t to check config syntax
Rate limiting not workingZone misconfigurationVerify zone names match in limit_req_zone and limit_req directives
Legitimate users getting blockedRate limits too strictIncrease burst values and adjust rates based on traffic analysis
GeoIP blocking not workingMissing GeoIP databaseInstall GeoIP database: sudo apt install geoip-database
High memory usageRate limiting zones too largeReduce zone sizes or implement zone cleanup
Logs not rotatingLogrotate configuration errorTest logrotate: sudo logrotate -d /etc/logrotate.d/nginx-security
429 errors for API clientsBurst setting too lowIncrease burst parameter for API endpoints
Monitoring script not runningPermissions or cron issuesCheck script permissions and cron logs: sudo journalctl -u cron
Never use chmod 777. Rate limiting zones and log files should use specific permissions like 644 for files and 755 for directories. Use chown to set proper ownership to www-data user instead.

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.