How to Implement Automatic IP Rate Limiting on LAMP Server to Prevent HTTP Flood Attacks


2 views

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
    }
  }
)