The Complete NGINX on Ubuntu Series: Part 11 – Rate Limiting and Traffic Control

The Complete NGINX on Ubuntu Series: Part 11 – Rate Limiting and Traffic Control

Welcome to Part 11 of our comprehensive NGINX on Ubuntu series! We’ll implement advanced rate limiting and traffic control mechanisms to protect your server from abuse, manage bandwidth, and ensure fair resource allocation.

Rate Limiting Fundamentals

Rate limiting controls the frequency of requests from clients, protecting your server from overload, DDoS attacks, and ensuring quality of service for all users.

graph TD
    A[Incoming Requests] --> B[Rate Limiting Zones]
    B --> C{Within Limits?}
    
    C -->|Yes| D[Process Request]
    C -->|No| E[Rate Limit Action]
    
    E --> F[Reject - 429 Error]
    E --> G[Delay - Queue Request]
    E --> H[Log - Security Event]
    
    I[Rate Limiting Types] --> J[Request Rate]
    I --> K[Connection Rate]
    I --> L[Bandwidth Rate]
    I --> M[Concurrent Connections]
    
    style B fill:#e1f5fe
    style C fill:#fff3e0
    style D fill:#e8f5e8
    style E fill:#ffebee
    style I fill:#e8f5e8

Basic Rate Limiting Setup

# Configure rate limiting zones in nginx.conf
sudo nano /etc/nginx/nginx.conf
# Add to http block in nginx.conf
http {
    # Basic rate limiting zones
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
    limit_req_zone $binary_remote_addr zone=search:10m rate=5r/s;
    limit_req_zone $binary_remote_addr zone=downloads:10m rate=2r/s;
    
    # Connection limiting zones
    limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
    limit_conn_zone $server_name zone=conn_per_server:10m;
    
    # Advanced rate limiting with custom keys
    limit_req_zone $binary_remote_addr$request_uri zone=per_uri:10m rate=5r/s;
    limit_req_zone $binary_remote_addr$http_user_agent zone=per_agent:10m rate=10r/s;
    
    # Session-based rate limiting
    limit_req_zone $cookie_sessionid zone=per_session:10m rate=30r/s;
    
    # Rate limiting status codes
    limit_req_status 429;
    limit_conn_status 503;
}

Advanced Rate Limiting Configuration

# Create advanced rate limiting virtual host
sudo nano /etc/nginx/sites-available/rate-limited.example.com
server {
    listen 80;
    server_name rate-limited.example.com;
    root /var/www/rate-limited/public_html;
    
    # Global connection limits
    limit_conn conn_per_ip 20;
    limit_conn conn_per_server 1000;
    
    # Error pages for rate limiting
    error_page 429 /429.html;
    error_page 503 /503.html;
    
    # Main application with general rate limiting
    location / {
        limit_req zone=general burst=20 nodelay;
        limit_req zone=per_uri burst=5 nodelay;
        
        try_files $uri $uri/ =404;
        
        # Add rate limiting headers
        add_header X-RateLimit-Zone "general" always;
        add_header X-RateLimit-Remaining $limit_req_remaining always;
    }
    
    # API endpoints with specific limits
    location /api/v1/ {
        # Multiple rate limiting zones
        limit_req zone=api burst=50 nodelay;
        limit_req zone=per_session burst=100 nodelay;
        
        # Connection limits for APIs
        limit_conn conn_per_ip 10;
        
        proxy_pass http://api_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # API rate limiting headers
        add_header X-RateLimit-Zone "api" always;
        add_header X-RateLimit-Limit "20/s" always;
        add_header X-RateLimit-Remaining $limit_req_remaining always;
    }
    
    # Login/Authentication with strict limits
    location ~ ^/(login|register|forgot-password) {
        limit_req zone=login burst=3 nodelay;
        limit_conn conn_per_ip 5;
        
        # Additional security for auth endpoints
        client_body_buffer_size 1k;
        client_max_body_size 1k;
        
        try_files $uri $uri/ =404;
        
        # Log authentication attempts
        access_log /var/log/nginx/auth-attempts.log combined;
    }
    
    # Search functionality with moderate limits
    location /search {
        limit_req zone=search burst=10 delay=5;
        
        # Prevent search abuse
        if ($args ~ "(.{100,})") {
            return 400 "Query too long";
        }
        
        proxy_pass http://search_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Search-specific headers
        add_header X-Search-RateLimit "5/s" always;
    }
    
    # File downloads with bandwidth limiting
    location /downloads/ {
        limit_req zone=downloads burst=1 nodelay;
        limit_conn conn_per_ip 2;
        
        # Bandwidth limiting for downloads
        limit_rate 1m;  # 1MB/s per connection
        limit_rate_after 10m;  # Start limiting after 10MB
        
        try_files $uri =404;
        
        # Download tracking
        access_log /var/log/nginx/downloads.log combined;
        add_header X-Download-Rate "1MB/s" always;
    }
    
    # Static files with relaxed limits
    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        limit_req zone=general burst=100 nodelay;
        
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }
    
    # Admin area with IP-based limits
    location /admin/ {
        # Strict rate limiting for admin
        limit_req zone=login burst=5 delay=2;
        limit_conn conn_per_ip 3;
        
        auth_basic "Admin Area";
        auth_basic_user_file /etc/nginx/auth/.htpasswd;
        
        try_files $uri $uri/ =404;
    }
}

Dynamic Rate Limiting

# Create dynamic rate limiting configuration
sudo nano /etc/nginx/sites-available/dynamic-rates.example.com
# Dynamic rate limiting based on conditions
map $http_user_agent $rate_limit_key {
    default $binary_remote_addr;
    ~*bot "";  # Bypass rate limiting for legitimate bots
    ~*google "";
    ~*bing "";
}

map $http_x_api_key $api_rate_limit {
    default "1r/s";
    "premium-key-123" "100r/s";
    "standard-key-456" "50r/s";
    "basic-key-789" "10r/s";
}

map $time_iso8601 $peak_hours {
    default 0;
    ~*T(09|10|11|12|13|14|15|16|17): 1;  # Peak hours 9 AM - 5 PM
}

# Rate limiting zones for different conditions
limit_req_zone $rate_limit_key zone=dynamic_general:10m rate=10r/s;
limit_req_zone $rate_limit_key zone=peak_hours:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=premium_api:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=standard_api:10m rate=50r/s;
limit_req_zone $binary_remote_addr zone=basic_api:10m rate=10r/s;

server {
    listen 80;
    server_name dynamic-rates.example.com;
    root /var/www/dynamic-rates/public_html;
    
    # Dynamic rate limiting based on time
    location / {
        if ($peak_hours = 1) {
            limit_req zone=peak_hours burst=10 nodelay;
        }
        if ($peak_hours = 0) {
            limit_req zone=dynamic_general burst=20 nodelay;
        }
        
        try_files $uri $uri/ =404;
        
        add_header X-Peak-Hours $peak_hours always;
    }
    
    # API with tiered rate limiting
    location /api/ {
        # Set rate limit based on API key
        set $api_zone "basic_api";
        
        if ($http_x_api_key = "premium-key-123") {
            set $api_zone "premium_api";
        }
        if ($http_x_api_key = "standard-key-456") {
            set $api_zone "standard_api";
        }
        
        # Apply appropriate rate limit
        limit_req zone=$api_zone burst=20 nodelay;
        
        proxy_pass http://api_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-API-Key $http_x_api_key;
        
        add_header X-API-Zone $api_zone always;
        add_header X-Rate-Limit $api_rate_limit always;
    }
}

Bandwidth Control and Traffic Shaping

graph TD
    A[Traffic Shaping] --> B[Bandwidth Limiting]
    A --> C[Connection Limiting]
    A --> D[Request Queuing]
    A --> E[Priority Management]
    
    B --> F[Per-Connection Limits
Global Bandwidth
Progressive Limiting] C --> G[Max Connections
Per-IP Limits
Server Limits] D --> H[Request Buffering
Queue Management
Burst Handling] E --> I[VIP Users
API Tiers
Content Priority] style A fill:#e1f5fe style F fill:#fff3e0 style G fill:#e3f2fd style H fill:#e8f5e8 style I fill:#fff3e0
# Create bandwidth control configuration
sudo nano /etc/nginx/sites-available/bandwidth-control.example.com
server {
    listen 80;
    server_name bandwidth-control.example.com;
    root /var/www/bandwidth-control/public_html;
    
    # Global bandwidth settings
    limit_conn conn_per_ip 10;
    limit_conn conn_per_server 1000;
    
    # Main content with standard bandwidth
    location / {
        limit_req zone=general burst=20 nodelay;
        
        try_files $uri $uri/ =404;
    }
    
    # Video streaming with progressive bandwidth control
    location /videos/ {
        limit_req zone=downloads burst=2 nodelay;
        limit_conn conn_per_ip 3;
        
        # Progressive bandwidth limiting
        limit_rate 2m;        # Start at 2MB/s
        limit_rate_after 50m; # After 50MB, apply limit
        
        # Video-specific headers
        add_header Accept-Ranges bytes;
        add_header Cache-Control "public, max-age=3600";
        
        try_files $uri =404;
    }
    
    # File downloads with different tiers
    location /premium-downloads/ {
        # Premium users get higher bandwidth
        limit_req zone=downloads burst=5 nodelay;
        limit_conn conn_per_ip 5;
        limit_rate 5m;  # 5MB/s for premium
        
        # Premium user authentication required
        auth_basic "Premium Downloads";
        auth_basic_user_file /etc/nginx/auth/premium.htpasswd;
        
        try_files $uri =404;
        
        access_log /var/log/nginx/premium-downloads.log;
    }
    
    location /standard-downloads/ {
        # Standard users get moderate bandwidth
        limit_req zone=downloads burst=2 nodelay;
        limit_conn conn_per_ip 2;
        limit_rate 1m;  # 1MB/s for standard
        
        try_files $uri =404;
        
        access_log /var/log/nginx/standard-downloads.log;
    }
    
    location /free-downloads/ {
        # Free users get limited bandwidth
        limit_req zone=downloads burst=1 nodelay;
        limit_conn conn_per_ip 1;
        limit_rate 512k;  # 512KB/s for free
        limit_rate_after 10m;  # Start limiting after 10MB
        
        try_files $uri =404;
        
        access_log /var/log/nginx/free-downloads.log;
    }
    
    # API with bandwidth awareness
    location /api/ {
        limit_req zone=api burst=50 nodelay;
        
        # Limit response size for API
        client_max_body_size 1m;
        
        proxy_pass http://api_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Proxy buffering for bandwidth control
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        proxy_busy_buffers_size 8k;
    }
    
    # Large file uploads with progressive limits
    location /upload/ {
        limit_req zone=general burst=5 delay=2;
        limit_conn conn_per_ip 2;
        
        # Upload size limits
        client_max_body_size 100m;
        client_body_buffer_size 1m;
        client_body_timeout 300s;
        
        # Slow down large uploads
        if ($content_length ~ "^[5-9][0-9]{7}") {  # >50MB
            limit_rate 512k;
        }
        
        proxy_pass http://upload_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Upload timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
    }
}

Rate Limiting Monitoring

# Create rate limiting monitor script
sudo nano /usr/local/bin/nginx-rate-monitor.sh
#!/bin/bash

# NGINX Rate Limiting Monitor
ACCESS_LOG="/var/log/nginx/access.log"
ERROR_LOG="/var/log/nginx/error.log"

monitor_rate_limiting() {
    echo "=== NGINX Rate Limiting Monitor ==="
    echo "Generated: $(date)"
    echo
    
    # Current rate limiting events
    echo "--- Rate Limiting Events (Last Hour) ---"
    local hour_ago=$(date -d '1 hour ago' '+%d/%b/%Y:%H')
    local current_hour=$(date '+%d/%b/%Y:%H')
    
    # Count 429 responses (rate limited)
    local rate_limited=$(grep -E "$hour_ago|$current_hour" "$ACCESS_LOG" 2>/dev/null | grep " 429 " | wc -l)
    echo "Rate limited requests: $rate_limited"
    
    # Count 503 responses (connection limited)
    local conn_limited=$(grep -E "$hour_ago|$current_hour" "$ACCESS_LOG" 2>/dev/null | grep " 503 " | wc -l)
    echo "Connection limited requests: $conn_limited"
    echo
    
    # Top rate limited IPs
    echo "--- Top Rate Limited IPs ---"
    grep -E "$hour_ago|$current_hour" "$ACCESS_LOG" 2>/dev/null | grep " 429 " | awk '{print $1}' | sort | uniq -c | sort -nr | head -5
    echo
    
    # Rate limiting errors from error log
    echo "--- Rate Limiting Errors (Last 5) ---"
    grep "limiting" "$ERROR_LOG" 2>/dev/null | tail -5
    echo
    
    # Connection statistics
    echo "--- Current Connections ---"
    local total_connections=$(ss -tun | grep ':80\|:443' | wc -l)
    local established=$(ss -tun | grep ESTAB | grep ':80\|:443' | wc -l)
    echo "Total connections: $total_connections"
    echo "Established connections: $established"
    echo
    
    # Request rate (last minute)
    echo "--- Request Rate (Last Minute) ---"
    local current_minute=$(date '+%d/%b/%Y:%H:%M')
    local rpm=$(grep "$current_minute" "$ACCESS_LOG" 2>/dev/null | wc -l)
    echo "Requests per minute: $rpm"
}

case "${1:-monitor}" in
    monitor)
        monitor_rate_limiting
        ;;
    watch)
        while true; do
            clear
            monitor_rate_limiting
            echo "Press Ctrl+C to exit..."
            sleep 10
        done
        ;;
    *)
        echo "Usage: $0 {monitor|watch}"
        ;;
esac

# Make executable: sudo chmod +x /usr/local/bin/nginx-rate-monitor.sh

Testing and Validation

# Enable rate limited sites
sudo ln -s /etc/nginx/sites-available/rate-limited.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/dynamic-rates.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/bandwidth-control.example.com /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload NGINX
sudo systemctl reload nginx

# Test rate limiting
echo "Testing rate limiting..."
for i in {1..15}; do
    curl -H "Host: rate-limited.example.com" http://localhost/ -w "%{http_code}\n" -o /dev/null -s
    sleep 0.1
done

# Test API rate limiting with different keys
curl -H "Host: dynamic-rates.example.com" -H "X-API-Key: premium-key-123" http://localhost/api/ -v
curl -H "Host: dynamic-rates.example.com" -H "X-API-Key: basic-key-789" http://localhost/api/ -v

# Test bandwidth limiting
curl -H "Host: bandwidth-control.example.com" http://localhost/downloads/testfile -o /dev/null -w "Speed: %{speed_download} bytes/sec\n"

# Monitor rate limiting
/usr/local/bin/nginx-rate-monitor.sh

# Watch rate limiting in real-time
/usr/local/bin/nginx-rate-monitor.sh watch

# Load test to trigger rate limits
ab -n 100 -c 20 -H "Host: rate-limited.example.com" http://localhost/

# Check rate limiting logs
grep "429\|503" /var/log/nginx/access.log | tail -10

What’s Next?

Excellent! You’ve implemented comprehensive rate limiting and traffic control mechanisms. Your NGINX server can now handle traffic spikes, prevent abuse, and ensure fair resource allocation with sophisticated rate limiting strategies.

Coming up in Part 12: NGINX WebSocket Support and Real-time Applications

References


This is Part 11 of our 22-part NGINX series. Your server now has enterprise-grade traffic control! Next, we’ll add WebSocket support for real-time applications. Questions? Share them in the comments!

Written by:

472 Posts

View All Posts
Follow Me :