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.comserver {
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.comserver {
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.shTesting 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 -10What’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!
