Skip to main content

Rate Limiting with Apache 2: A Practical Guide to Protecting Your Web Applications

Rate limiting is one of the most effective defensive measures you can implement to protect web applications from abuse, brute force attacks, and denial of service attempts. Apache 2 provides several powerful modules for implementing rate limiting, each suited to different use cases and threat models.

Why Rate Limiting Matters

Before diving into configuration, it’s worth understanding what you’re defending against. Uncontrolled request rates can lead to resource exhaustion on your server, successful credential stuffing attacks against login forms, API abuse that affects legitimate users, and scraping that consumes bandwidth and potentially exposes data you’d prefer to keep harder to harvest at scale.

The Primary Tools: mod_ratelimit vs mod_evasive vs mod_qos

Apache offers several approaches to rate limiting, and choosing the right one depends on your specific requirements.

mod_ratelimit: Bandwidth Throttling

This module limits the bandwidth of a connection rather than the number of requests. It’s ideal for controlling download speeds or preventing a single client from saturating your connection.

<Location "/downloads">
    SetOutputFilter RATE_LIMIT
    SetEnv rate-limit 400
    SetEnv rate-initial-burst 512
</Location>

The rate-limit value is in KB/s, so this configuration limits downloads to approximately 400 KB/s after an initial burst of 512 KB. This approach works well for file servers or media delivery where you want fair bandwidth distribution.

mod_evasive: DDoS and Brute Force Protection

For request-based rate limiting, mod_evasive is the traditional choice. It tracks requests per IP and can temporarily blacklist offenders.

First, install it on Debian/Ubuntu systems:

apt install libapache2-mod-evasive
a2enmod evasive

Then configure it:

<IfModule mod_evasive20.c>
    DOSHashTableSize    3097
    DOSPageCount        5
    DOSSiteCount        100
    DOSPageInterval     1
    DOSSiteInterval     1
    DOSBlockingPeriod   60
    
    DOSEmailNotify      [email protected]
    DOSSystemCommand    "sudo /usr/local/bin/ban_ip.sh %s"
    DOSLogDir           "/var/log/mod_evasive"
    
    DOSWhitelist        127.0.0.1
    DOSWhitelist        192.168.1.*
</IfModule>

Breaking down these directives: DOSPageCount and DOSPageInterval work together—if a client requests the same page more than 5 times in 1 second, they’re blocked. DOSSiteCount and DOSSiteInterval set the threshold for total requests to any page on the site. DOSBlockingPeriod determines how long offenders are blocked in seconds.

The DOSSystemCommand directive is particularly powerful for integration with firewall rules. A simple ban script might look like:

#!/bin/bash
# /usr/local/bin/ban_ip.sh
/sbin/iptables -I INPUT -s $1 -j DROP
# Auto-unban after 10 minutes
echo "/sbin/iptables -D INPUT -s $1 -j DROP" | at now + 10 minutes

mod_qos: Fine-Grained Quality of Service

For more sophisticated rate limiting, mod_qos provides granular control. It can limit concurrent connections, request rates, and even bandwidth on a per-location or per-client basis.

<IfModule mod_qos.c>
    # Maximum concurrent connections per IP
    QS_SrvMaxConnPerIP          50
    
    # Maximum concurrent connections total
    QS_SrvMaxConn               500
    
    # Limit request rate per second per IP for specific locations
    <Location "/api">
        QS_LocRequestLimitMatch       "^.*$" 50
        QS_LocRequestPerSecLimitMatch "^.*$" 10
    </Location>
    
    # Slow down connections that exceed thresholds rather than blocking
    QS_SrvMinDataRate             150 1200
</IfModule>

The QS_SrvMinDataRate directive is elegant—it sets minimum data rates (150 bytes/s with a max of 1200 connections) and slows down connections that fall below this threshold, which helps identify and throttle slowloris-style attacks.

Protecting Specific Endpoints

Real-world rate limiting often needs to be surgical. Login pages, API endpoints, and form submission handlers typically need stricter limits than static content.

Login Page Protection

<Location "/wp-login.php">
    <IfModule mod_qos.c>
        QS_LocRequestPerSecLimitMatch "^.*$" 2
        QS_LocRequestLimitMatch       "^.*$" 10
    </IfModule>
</Location>

<Location "/user/login">
    <IfModule mod_qos.c>
        QS_LocRequestPerSecLimitMatch "^.*$" 2
        QS_LocRequestLimitMatch       "^.*$" 10
    </IfModule>
</Location>

API Rate Limiting with Custom Headers

For APIs, you’ll want to communicate rate limit status to clients. This requires combining mod_headers with your rate limiting:

<Location "/api/v1">
    <IfModule mod_qos.c>
        QS_LocRequestPerSecLimitMatch "^.*$" 30
    </IfModule>
    
    <IfModule mod_headers.c>
        Header always set X-RateLimit-Limit "30"
        Header always set Retry-After "60" "expr=%{REQUEST_STATUS} == 429"
    </IfModule>
</Location>

Using mod_rewrite for Conditional Rate Limiting

Sometimes you need rate limiting logic that the dedicated modules don’t handle well. mod_rewrite combined with environment variables can fill gaps:

<IfModule mod_rewrite.c>
    RewriteEngine On
    
    # Rate limit POST requests more aggressively
    RewriteCond %{REQUEST_METHOD} POST
    RewriteCond %{REQUEST_URI} ^/api/
    RewriteRule .* - [E=RATE_LIMIT_STRICT:1]
    
    # Skip rate limiting for authenticated internal services
    RewriteCond %{HTTP:X-Internal-Service-Key} ^your-secret-key$
    RewriteRule .* - [E=RATE_LIMIT_BYPASS:1]
</IfModule>

<If "-n %{ENV:RATE_LIMIT_STRICT} && ! -n %{ENV:RATE_LIMIT_BYPASS}">
    <IfModule mod_qos.c>
        QS_LocRequestPerSecLimitMatch "^.*$" 5
    </IfModule>
</If>

Fail2Ban Integration

For defense in depth, integrate Apache rate limiting with fail2ban. Create a filter that matches mod_evasive log entries:

# /etc/fail2ban/filter.d/apache-evasive.conf
[Definition]
failregex = ^<HOST> was blocked by mod_evasive
ignoreregex =

And the corresponding jail:

# /etc/fail2ban/jail.local

[apache-evasive]

enabled = true port = http,https filter = apache-evasive logpath = /var/log/mod_evasive/dos-* maxretry = 1 bantime = 3600

Performance Considerations

Rate limiting modules consume memory and CPU cycles. A few optimizations help:

Keep the hash table size in mod_evasive as a prime number and sized appropriately for your traffic volume. Too small wastes CPU on collisions; too large wastes memory.

Consider using mod_qos’s QS_ClientEntries directive to limit tracking memory:

QS_ClientEntries          100000
QS_ClientPrefer           "^192\.168\."

For high-traffic sites, offload rate limiting to a reverse proxy like nginx or a CDN where possible. Apache can then handle rate limiting for traffic that bypasses those layers.

Testing Your Configuration

Before deploying rate limiting to production, test it. Apache Bench works for basic testing:

ab -n 1000 -c 100 https://yoursite.com/api/endpoint

For more realistic testing, use tools like siege or locust that can simulate varied request patterns:

siege -c 50 -t 60s -v https://yoursite.com/login

Watch your logs during testing:

tail -f /var/log/apache2/error.log /var/log/mod_evasive/*

Common Pitfalls

A few things I’ve seen go wrong in production deployments: setting thresholds too low and blocking legitimate users behind corporate NATs, forgetting to whitelist monitoring services and load balancer health checks, not accounting for AJAX-heavy applications that make many requests per page load, and failing to monitor false positives after deployment.

Start with permissive thresholds, monitor for a week, then tighten based on observed traffic patterns.

Conclusion

Effective rate limiting is about layering defenses. Use mod_ratelimit for bandwidth control, mod_evasive for basic DDoS protection, and mod_qos when you need fine-grained control. Combine these with fail2ban for persistent offenders and always test thoroughly before production deployment.

The configurations here provide a solid foundation, but every environment is different. Monitor your logs, adjust thresholds based on real traffic patterns, and remember that rate limiting is one part of a broader security strategy that should include WAF rules, proper authentication, and regular security audits.

No Comments yet!

Your Email address will not be published.