Limit multiple attachment downloads in WordPress by IP

This solution uses Fail2ban along with a separte PHP script and a MySQL database to limit the number of attachment downloads for an IP.

I needed this solution because some IP’s (mostly China) are constanty downloading the same files (zip and pdf) over and over again. In fact, the downloads are started but they never finish.

The disadvantages are :

  • you must use a different upload folder for your blog attachments.
  • you can allow an IP to download a certain file just once in a desired interval.

The advantages are :

  • you may ban the IP if it exceeds a number of retries.


– create a new folder in the root of your WordPress folder. I called it wp_uploads. This folder will contain all your uploads that need to be limited.

– create a .htaccess in wp_uploads with the following content :

RewriteEngine on
RewriteRule (.*)(pdf|zip)$ /wp_uploads/dld.php [QSA]

File .htaccess contains the type of files we want to limit the access for, in this case pdf and zip.

– create a dld.php file in wp_uploads with the following content :

// change the user, password, and database name in the next line
$cs_conn = mysql_connect('localhost', 'dld_user', 'secretpass');
mysql_select_db('download_db', $cs_conn);

mysql_query("CREATE TABLE IF NOT EXISTS `downloaded` (
 `filepath` varchar(255) NOT NULL,
 `ipadres` varchar(15) NOT NULL,
 `last_access` datetime NOT NULL,
 UNIQUE KEY `filepath` (`filepath`,`ipadres`),
 KEY `ipadres` (`ipadres`)

$path = addslashes($_SERVER['REQUEST_URI']);
$ip = addslashes($_SERVER['REMOTE_ADDR']);
$dl = false;

$sql = sprintf("SELECT UNIX_TIMESTAMP(last_access) last_time FROM downloaded WHERE filepath = '%s' AND ipadres = '%s' ORDER BY last_access DESC", $path, $ip);
$res = mysql_query($sql);
if (mysql_num_rows($res) > 0) {
 $last_xs = mysql_result($res, 0, 'last_time')+1800;
 $last = $last_xs;
 if ($last_xs < time()) {
 mysql_query(sprintf("REPLACE downloaded SET filepath = '%s', ipadres = '%s', last_access = NOW()", $path, $ip));
 $dl = true;
} else {
 $sql = sprintf("REPLACE downloaded SET filepath = '%s', ipadres = '%s', last_access = NOW()", $path, $ip);
 $dl = true;

if ($dl) {
 $fullPath = $_SERVER['DOCUMENT_ROOT'].$path;
 if ($fd = fopen ($fullPath, "r")) {
 $fname = basename($fullPath);
 header('Content-type: application/octet-stream');
 header('Content-Disposition: filename="'.$fname.'"');
 header('Content-length: '.filesize($fullPath));
 header('Cache-control: private');
 while(!feof($fd)) {
 $buffer = fread($fd, 2048);
 echo $buffer;
 fclose ($fd);
} elseĀ {

header('HTTP/1.0 503 Service Unavailable');
 die('Abort, you reached your download limit for this file. Try again in 30 minutes. Subsequent tries will ban your IP : '.$ip. '.Try after : '.date("H:i:s", $last));

For fail2ban we need to add the following jail in jail.local (this is for Ubuntu installations)

enabled = true
action = %(action_mwl)s
port = http
findtime = 600
filter = http-get-dos
logpath = /var/www/
maxretry = 5
bantime = 86400

#The IP gets banned for 1 day (86400 seconds) for 5 or more retries in 10 minutes for the same file.
#You will get an email with the log lines containing the IP
#access.log is my Apache log file
#http-get-dos is the name of the filter

In /etc/fail2ban/filter.d create a new file called http-get-dos.conf with the following content :

# Fail2Ban configuration file
# Author:

failregex = ^<HOST> -.*\"GET.*/wp_uploads.*503.*
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
ignoreregex =

Because the script returns a 503 error for trying to download the same file multiple times in 10 minutes, we can use this to trigger Fail2Ban.

Fail2ban log file content :

2014-02-27 09:23:34,263 fail2ban.actions: WARNING [http-get-dos] Ban
2014-02-27 09:54:47,239 fail2ban.actions: WARNING [http-get-dos] Ban

Leave a Reply

Your email address will not be published.


This site uses Akismet to reduce spam. Learn how your comment data is processed.