When a single IP address makes hundreds of requests per second - whether from a malicious bot or misconfigured script - it can cripple Apache's available worker threads and consume server RAM. Traditional firewall rules don't handle dynamic thresholds well, which is why we need application-aware rate limiting.
While PHP-based solutions exist (storing counters in sessions or databases), they add overhead exactly when your server is under stress. Apache's mod_evasive
and mod_security
provide kernel-level protection:
# Install mod_evasive on Debian/Ubuntu
sudo apt install libapache2-mod-evasive
# Configuration in /etc/apache2/mods-enabled/evasive.conf
DOSHashTableSize 3097
DOSPageCount 2
DOSSiteCount 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 300
For servers without module access, this bash script paired with cron checks netstat and blocks offenders:
#!/bin/bash
# Path to whitelist IPs
WHITELIST="/etc/iptables/whitelist.txt"
# Analyze connections
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n | while read -r line
do
COUNT=$(echo "$line" | awk '{print $1}')
IP=$(echo "$line" | awk '{print $2}')
# Skip localhost and whitelisted IPs
[[ "$IP" == "127.0.0.1" ]] || grep -q "$IP" "$WHITELIST" && continue
if [ "$COUNT" -gt 100 ]; then
echo "[$(date)] Blocking $IP ($COUNT connections)" >> /var/log/iptables.log
iptables -A INPUT -s "$IP" -j DROP
fi
done
Combine Apache logging with Fail2Ban's regex pattern matching for smarter detection:
# /etc/fail2ban/jail.local
[apache-overflood]
enabled = true
port = http,https
filter = apache-overflood
logpath = /var/log/apache2/access.log
maxretry = 100
findtime = 60
bantime = 3600
# /etc/fail2ban/filter.d/apache-overflood.conf
[Definition]
failregex = ^ .* "(GET|POST).*HTTP.*" 200
ignoreregex = .(css|jpg|png|js|woff2)
For those using Cloudflare, enable "Rate Limiting" rules in the dashboard with thresholds like:
- 100 requests/10 seconds
- Challenge (not block) suspicious traffic
- Apply to admin paths like /wp-admin/*
Use this command to audit current blocks:
sudo iptables -L -n -v | grep DROP
Consider logging to a separate partition to prevent log-based DoS attacks. The key is implementing layers - web server first, then OS-level, with whitelisting for critical IPs.
When a single IP bombards your LAMP server with rapid-fire requests (commonly seen in vulnerability scanners or brute force attacks), it can exhaust server resources. Apache's access logs typically show patterns like:
192.168.1.100 - - [01/Jan/2023:00:00:01] "GET /wp-admin.php HTTP/1.1" 404
192.168.1.100 - - [01/Jan/2023:00:00:02] "GET /admin.php HTTP/1.1" 404
192.168.1.100 - - [01/Jan/2023:00:00:03] "GET /login.php HTTP/1.1" 404
The most efficient solution operates at the OS level using Fail2Ban. This Python-based daemon monitors logs and dynamically updates firewall rules. For LAMP servers:
# Install Fail2Ban
sudo apt-get install fail2ban
# Create custom filter for Apache
sudo nano /etc/fail2ban/filter.d/apache-repeater.conf
[Definition]
failregex = ^<HOST>.*"(GET|POST).*HTTP.*" (404|403)
ignoreregex =
Configure the jail:
[apache-repeater]
enabled = true
port = http,https
filter = apache-repeater
logpath = /var/log/apache2/access.log
maxretry = 30
findtime = 60
bantime = 3600
For more granular control, ModSecurity with OWASP CRS rules provides request inspection:
# Install ModSecurity
sudo apt-get install libapache2-mod-security2
# Configure rate limiting
SecRule IP:REPEATER "@gt 50" "phase:1,id:1000,deny,status:429,\
msg:'Too many requests',setvar:ip.repeater=+1,expirevar:ip.repeater=60"
For cases requiring application context, implement a simple rate limiter in PHP:
$redis = new Redis();
$redis->connect('127.0.0.1');
$ip = $_SERVER['REMOTE_ADDR'];
$key = "rate_limit:$ip";
$current = $redis->incr($key);
if ($current > 60) {
header('HTTP/1.1 429 Too Many Requests');
die();
}
if ($current === 1) {
$redis->expire($key, 60);
}
For production environments, consider Cloudflare's Rate Limiting:
# Cloudflare WAF Rule example
cf.zone.rate_limit.create(
zone_id="YOUR_ZONE_ID",
data={
"description": "Block aggressive scanners",
"match": {
"request": {
"methods": ["GET", "POST"],
"schemes": ["HTTP", "HTTPS"],
"url_pattern": "/*"
}
},
"threshold": 50,
"period": 60,
"action": {
"mode": "ban",
"timeout": 3600
}
}
)