Implement Caddy 2 rate limiting and DDoS protection with advanced security rules

Intermediate 45 min Apr 26, 2026 653 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure Caddy 2 web server with comprehensive rate limiting, request throttling, and DDoS protection using built-in security modules and advanced filtering rules.

Prerequisites

  • Root or sudo access
  • Domain name pointed to your server
  • Basic understanding of web server configuration

What this solves

Caddy 2 provides built-in rate limiting and DDoS protection capabilities that help protect your web applications from malicious traffic and resource exhaustion attacks. This tutorial shows you how to implement comprehensive security rules including request rate limiting, connection throttling, IP-based filtering, and automated threat response to keep your services available under attack conditions.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you have the latest security updates.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install Caddy 2 with security modules

Install Caddy 2 from the official repository to get the latest version with all security features enabled.

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
sudo dnf install -y yum-utils
sudo yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/caddy/caddy/repo/epel-8/caddy-caddy-epel-8.repo
sudo dnf install -y caddy

Create directory structure

Set up the necessary directories for Caddy configuration and security rules with proper permissions.

sudo mkdir -p /etc/caddy/security
sudo mkdir -p /var/log/caddy
sudo mkdir -p /var/www/html
sudo chown -R caddy:caddy /var/log/caddy
sudo chmod 755 /etc/caddy /etc/caddy/security

Configure basic rate limiting

Create a Caddyfile with fundamental rate limiting rules that protect against basic flooding attacks.

{
    # Global rate limiting options
    order rate_limit before basicauth
    servers {
        max_header_bytes 16KB
    }
}

example.com {
    # Enable access logging
    log {
        output file /var/log/caddy/access.log
        format single_field common_log
    }
    
    # Basic rate limiting - 100 requests per minute per IP
    rate_limit {
        zone static_ip {
            key {remote_host}
            events 100
            window 1m
        }
        
        # More restrictive for API endpoints
        zone api_ip {
            key {remote_host}
            events 20
            window 1m
        }
    }
    
    # Apply rate limits to specific paths
    @api path /api/*
    rate_limit @api api_ip
    
    # Default rate limit for all other requests
    rate_limit static_ip
    
    # Root directory
    root * /var/www/html
    file_server
}

Add advanced DDoS protection rules

Enhance the configuration with sophisticated protection against various types of DDoS attacks.

{
    order rate_limit before basicauth
    order request_body before rate_limit
    
    servers {
        max_header_bytes 16KB
        read_timeout 30s
        read_header_timeout 10s
        write_timeout 60s
        idle_timeout 120s
    }
}

example.com {
    log {
        output file /var/log/caddy/access.log
        format single_field common_log
    }
    
    # Request body size limits to prevent large payload attacks
    request_body {
        max_size 10MB
    }
    
    # Multiple rate limiting zones for different threat levels
    rate_limit {
        # Standard web traffic - 200 req/min per IP
        zone web_traffic {
            key {remote_host}
            events 200
            window 1m
        }
        
        # API endpoints - 50 req/min per IP
        zone api_strict {
            key {remote_host}
            events 50
            window 1m
        }
        
        # Authentication endpoints - 10 req/min per IP
        zone auth_endpoints {
            key {remote_host}
            events 10
            window 1m
        }
        
        # Search/query endpoints - 30 req/min per IP
        zone search_endpoints {
            key {remote_host}
            events 30
            window 1m
        }
        
        # File upload endpoints - 5 req/min per IP
        zone upload_endpoints {
            key {remote_host}
            events 5
            window 1m
        }
    }
    
    # Define request matchers for different endpoint types
    @auth path /login* /register* /auth/* /oauth/*
    @api path /api/*
    @search path /search* /query*
    @upload path /upload* /files/*
    @static path *.css *.js *.png *.jpg *.gif *.ico *.woff* *.ttf
    
    # Apply appropriate rate limits
    rate_limit @auth auth_endpoints
    rate_limit @api api_strict
    rate_limit @search search_endpoints
    rate_limit @upload upload_endpoints
    
    # Lenient rate limiting for static assets
    @static_with_rate {
        path *.css *.js *.png *.jpg *.gif *.ico *.woff* *.ttf
    }
    rate_limit @static_with_rate {
        zone static_assets {
            key {remote_host}
            events 500
            window 1m
        }
    }
    
    # Default rate limit for other requests
    rate_limit web_traffic
    
    root * /var/www/html
    file_server
}

Implement IP blocking and security headers

Add IP-based access control and security headers to strengthen protection against attacks.

# Blocked IP addresses - one per line
# 192.168.1.100
</code><h2>`10.0.0.50`</h2></pre>

<pre class="terminal"><code>{
    order rate_limit before basicauth
    order request_body before rate_limit
    
    servers {
        max_header_bytes 16KB
        read_timeout 30s
        read_header_timeout 10s
        write_timeout 60s
        idle_timeout 120s
    }
}

example.com {
    log {
        output file /var/log/caddy/access.log
        format single_field common_log
    }
    
    # Import blocked IPs (create matcher for blocked IPs)
    @blocked_ips {
        remote_ip 192.0.2.0/24 198.51.100.0/24
    }
    
    # Immediately respond with 403 for blocked IPs
    respond @blocked_ips "Access Denied" 403
    
    # Block requests with suspicious User-Agent strings
    @malicious_ua {
        header User-Agent *bot* *crawler* *scanner* *nikto* *sqlmap*
    }
    respond @malicious_ua "Forbidden" 403
    
    # Block requests with no User-Agent
    @no_ua {
        not header User-Agent *
    }
    respond @no_ua "Bad Request" 400
    
    # Security headers for all responses
    header {
        # Prevent clickjacking
        X-Frame-Options DENY
        
        # Prevent MIME type sniffing
        X-Content-Type-Options nosniff
        
        # Enable XSS protection
        X-XSS-Protection "1; mode=block"
        
        # Strict transport security
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        
        # Content security policy
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
        
        # Referrer policy
        Referrer-Policy "strict-origin-when-cross-origin"
        
        # Remove server information
        -Server
        -X-Powered-By
    }
    
    # Request body size limits
    request_body {
        max_size 10MB
    }
    
    # Enhanced rate limiting with multiple zones
    rate_limit {
        zone web_traffic {
            key {remote_host}
            events 200
            window 1m
        }
        
        zone api_strict {
            key {remote_host}
            events 50
            window 1m
        }
        
        zone auth_endpoints {
            key {remote_host}
            events 10
            window 1m
        }
        
        zone search_endpoints {
            key {remote_host}
            events 30
            window 1m
        }
        
        zone upload_endpoints {
            key {remote_host}
            events 5
            window 1m
        }
        
        # Burst protection - very restrictive short-term limit
        zone burst_protection {
            key {remote_host}
            events 10
            window 10s
        }
    }
    
    # Apply burst protection to all requests first
    rate_limit burst_protection
    
    # Path-specific rate limiting
    @auth path /login* /register* /auth/* /oauth/*
    @api path /api/*
    @search path /search* /query*
    @upload path /upload* /files/*
    
    rate_limit @auth auth_endpoints
    rate_limit @api api_strict
    rate_limit @search search_endpoints
    rate_limit @upload upload_endpoints
    rate_limit web_traffic
    
    root * /var/www/html
    file_server
}

Set up connection limits and timeouts

Configure connection-level protections to prevent resource exhaustion attacks.

{
    order rate_limit before basicauth
    order request_body before rate_limit
    
    # Global server settings with connection limits
    servers {
        max_header_bytes 16KB
        read_timeout 30s
        read_header_timeout 10s
        write_timeout 60s
        idle_timeout 120s
        
        # Connection limits per IP
        listener_wrappers {
            http_redirect
            tls
        }
        
        # Protocol settings
        protocols h1 h2
    }
    
    # Error handling
    handle_errors {
        @404 expression {http.error.status_code} == 404
        @429 expression {http.error.status_code} == 429
        @500 expression {http.error.status_code} >= 500
        
        handle @429 {
            respond "Rate limit exceeded. Please try again later." 429
        }
        
        handle @404 {
            respond "Page not found" 404
        }
        
        handle @500 {
            respond "Internal server error" 500
        }
    }
}

example.com {
    log {
        output file /var/log/caddy/access.log {
            roll_size 100MB
            roll_keep 10
        }
        format single_field common_log
        level INFO
    }
    
    # Enhanced IP blocking with network ranges
    @blocked_networks {
        remote_ip 192.0.2.0/24 198.51.100.0/24 203.0.113.0/24
    }
    respond @blocked_networks "Access Denied" 403
    
    # Block common attack patterns
    @attack_patterns {
        query *union* *select* *drop* *insert* *update* *delete*
    }
    respond @attack_patterns "Malicious request detected" 403
    
    # Block suspicious request methods
    @suspicious_methods {
        method TRACE OPTIONS CONNECT
    }
    respond @suspicious_methods "Method not allowed" 405
    
    # Comprehensive security headers
    header {
        X-Frame-Options DENY
        X-Content-Type-Options nosniff
        X-XSS-Protection "1; mode=block"
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; child-src 'none'; frame-src 'none'; worker-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "geolocation=(), microphone=(), camera=()"
        X-Permitted-Cross-Domain-Policies "none"
        -Server
        -X-Powered-By
    }
    
    # Request size and timeout limits
    request_body {
        max_size 10MB
    }
    
    # Multi-tier rate limiting system
    rate_limit {
        # Tier 1: Burst protection (very short window)
        zone burst_guard {
            key {remote_host}
            events 5
            window 1s
        }
        
        # Tier 2: Short-term protection
        zone short_term {
            key {remote_host}
            events 20
            window 10s
        }
        
        # Tier 3: Medium-term protection
        zone medium_term {
            key {remote_host}
            events 100
            window 1m
        }
        
        # Tier 4: Long-term protection
        zone long_term {
            key {remote_host}
            events 500
            window 5m
        }
        
        # Specialized zones
        zone api_endpoints {
            key {remote_host}
            events 30
            window 1m
        }
        
        zone auth_endpoints {
            key {remote_host}
            events 5
            window 1m
        }
        
        zone upload_endpoints {
            key {remote_host}
            events 3
            window 1m
        }
    }
    
    # Apply multi-tier rate limiting to all requests
    rate_limit burst_guard
    rate_limit short_term
    rate_limit medium_term
    rate_limit long_term
    
    # Path-specific additional limits
    @api path /api/*
    @auth path /login* /register* /auth/* /password-reset*
    @upload path /upload* /files/* /media/*
    
    rate_limit @api api_endpoints
    rate_limit @auth auth_endpoints
    rate_limit @upload upload_endpoints
    
    root * /var/www/html
    file_server
}

Enable and start Caddy service

Start Caddy and enable it to run automatically on system boot.

sudo systemctl daemon-reload
sudo systemctl enable caddy
sudo systemctl start caddy
sudo systemctl status caddy

Create test web content

Add some basic content to test the rate limiting and security features.

sudo tee /var/www/html/index.html > /dev/null << 'EOF'



    


    <h1>Caddy Server with DDoS Protection</h1>
    <p>This server is protected by Caddy's rate limiting and security features.</p>
    <ul>
        <li><a href="/api/test">API Endpoint (rate limited)</a></li>
        <li><a href="/search?q=test">Search Endpoint (rate limited)</a></li>
        <li><a href="/upload">Upload Endpoint (heavily rate limited)</a></li>
    </ul>


EOF

sudo chown -R caddy:caddy /var/www/html

Configure monitoring and alerting

Set up log monitoring

Create a script to monitor Caddy logs for rate limiting events and potential attacks.

#!/bin/bash

# Log file paths
ACCESS_LOG="/var/log/caddy/access.log"
ALERT_LOG="/var/log/caddy/security-alerts.log"
EMAIL="admin@example.com"

# Function to send alert
send_alert() {
    local message="$1"
    echo "$(date): $message" >> "$ALERT_LOG"
    # Uncomment to send email alerts
    # echo "$message" | mail -s "Caddy Security Alert" "$EMAIL"
}

# Monitor for rate limiting events
if [ -f "$ACCESS_LOG" ]; then
    # Check for 429 (Too Many Requests) responses in the last minute
    recent_429s=$(tail -n 1000 "$ACCESS_LOG" | grep "$(date -d '1 minute ago' '+%d/%b/%Y:%H:%M')" | grep -c " 429 ")
    
    if [ "$recent_429s" -gt 10 ]; then
        send_alert "High rate limiting activity detected: $recent_429s requests blocked in the last minute"
    fi
    
    # Check for blocked IPs (403 responses)
    recent_403s=$(tail -n 1000 "$ACCESS_LOG" | grep "$(date -d '1 minute ago' '+%d/%b/%Y:%H:%M')" | grep -c " 403 ")
    
    if [ "$recent_403s" -gt 5 ]; then
        send_alert "Suspicious activity detected: $recent_403s blocked requests in the last minute"
    fi
    
    # Check for potential DDoS (high request volume from single IPs)
    high_volume_ips=$(tail -n 5000 "$ACCESS_LOG" | grep "$(date '+%d/%b/%Y:%H:%M')" | awk '{print $1}' | sort | uniq -c | awk '$1 > 50 {print $2 " (" $1 " requests)"}')
    
    if [ -n "$high_volume_ips" ]; then
        send_alert "High volume requests detected from IPs: $high_volume_ips"
    fi
fi
sudo chmod +x /etc/caddy/security/monitor-attacks.sh

Set up automated monitoring cron job

Schedule the monitoring script to run every minute to detect attacks quickly.

sudo crontab -e

Add this line to run the monitoring script every minute:

* * * * * /etc/caddy/security/monitor-attacks.sh

Create IP blocking automation script

Set up a script that can automatically block IPs that exceed rate limits.

#!/bin/bash

ACCESS_LOG="/var/log/caddy/access.log"
BLOCKED_IPS_FILE="/etc/caddy/security/blocked-ips.txt"
CADDYFILE="/etc/caddy/Caddyfile"

# Find IPs with more than 1000 requests in the last hour
offending_ips=$(tail -n 10000 "$ACCESS_LOG" | grep "$(date -d '1 hour ago' '+%d/%b/%Y:%H')" | awk '{print $1}' | sort | uniq -c | awk '$1 > 1000 {print $2}')

for ip in $offending_ips; do
    # Check if IP is already blocked
    if ! grep -q "^$ip$" "$BLOCKED_IPS_FILE" 2>/dev/null; then
        echo "$ip" >> "$BLOCKED_IPS_FILE"
        echo "$(date): Auto-blocked IP $ip for excessive requests" >> /var/log/caddy/security-alerts.log
        
        # Update Caddyfile with new blocked IP
        # This is a simple approach - for production, consider a more sophisticated method
        sed -i "/remote_ip.*203\.0\.113\.0\/24/a\        remote_ip $ip" "$CADDYFILE"
        
        # Reload Caddy configuration
        systemctl reload caddy
    fi
done
sudo chmod +x /etc/caddy/security/auto-block.sh

Test rate limiting and security features

Test basic rate limiting

Use curl to test that rate limiting is working correctly by making rapid requests.

# Test burst protection (should start blocking after 5 requests per second)
for i in {1..10}; do
    curl -w "%{http_code}\n" -o /dev/null -s http://example.com/
done

# Test API rate limiting
for i in {1..25}; do
    curl -w "%{http_code}\n" -o /dev/null -s http://example.com/api/t

Automated install script

Run this to automate the entire setup

不想自己管理这些吗?

我们为依赖稳定运行时间的企业管理基础设施。全托管服务,配备一位熟悉您系统架构的固定联系人。

您将拥有一位了解您整体架构的固定联系人

Rotterdam 06:07 · 一条消息即可联系我们,无需填写工单表单