Take the 2-minute tour ×

I have found this script

Quick and easy flood protection?

and I have turned it into a function.

Works great for the most part. From time to time I see an error:


in line:

else if ($diff>3600) { unlink($path); } // If first request was more than 1 hour, new ip file

Apparently some IP files for some reason are getting deleted ?

I have tried to find the logic error, but I'm not good at all at that. Maybe somebody could help.

The function:

function ht_request_limiter() {
    if (!isset($_SERVER['REMOTE_ADDR'])) { return; } // Maybe its impossible, however we check it first
    $path = '/home/czivbaby/valuemarket.gr/ip-sec/'; // I use a function to validate a path first and return if false...
    $path = $path.$_SERVER['REMOTE_ADDR'].'.txt'; // Real file path (filename = <ip>.txt)
    $now = time(); // Current timestamp
    if (!file_exists($path)) { // If first request or new request after 1 hour / 24 hour ban, new file with <timestamp>|<counter>
        if ($handle = fopen($path, 'w+')) {
            if (fwrite($handle, $now.'|0')) { chmod($path, 0700); } // Chmod to prevent access via web
    else if (($content = file_get_contents($path)) !== false) { // Load existing file
        $content = explode('|',$content); // Create paraset [0] -> timestamp  [1] -> counter
        $diff = (int)$now-(int)$content[0]; // Time difference in seconds from first request to now
        if ($content[1] == 'ban') { // If [1] = ban we check if it was less than 24 hours and die if so
            if ($diff>86400) { unlink($path); } // 24 hours in seconds.. if more delete ip file
            else {
                header("HTTP/1.1 503 Service Unavailable");
                exit("Your IP is banned for 24 hours, because of too many requests.");
        else {
            $current = ((int)$content[1])+1; // Counter + 1
            if ($current>200) { // We check rpm (request per minute) after 200 request to get a good ~value
                $rpm = ($current/($diff/60));
                if ($rpm>10) { // If there was more than 10 rpm -> ban (if you have a request all 5 secs. you will be banned after ~17 minutes)
                        fwrite($handle, $content[0].'|ban');
                        // Maybe you like to log the ip once -> die after next request
            if ($handle = fopen($path, 'w+')) { // else write counter
                fwrite($handle, $content[0].'|'.$current .'');
share|improve this question

1 Answer 1

Your server is processing two (or more) requests at the same time from the same client, and the script does not seem to handle this (completely normal) situation correctly. Web browsers download multiple objects from a server in parallel in order to speed up browsing. It's quite likely that, every now and then, a browser does two requests which then end up executing in parallel so that two copies of that script end up at the same unlink() call at roughly the same time. One succeeds in deleting the file, and the other one gives the error message.

Even if your server has a single CPU, the operating system will be happily providing multitasking by context switching between multiple PHP processes which are executing the same PHP script at the same time for the same client IP address.

The script should probably use file locking (http://php.net/manual/en/function.flock.php) to lock the file while working on it. Or simply ignore the unlink() error (by placing a @ in front of the unlink), but other concurrency problems are likely to come up.

The script should:

  1. Open the file for reading and writing using $f = fopen($filename, 'r+');
  2. Lock the opened file using the file handle. The flock($f, LOCK_EX) call will block and wait if some other process already has a lock.
  3. Read file contents.
  4. Decide what to do (increment counter, refuse to service).
  5. fseek($f, 0, SEEK_SET) to beginning of file, ftruncate($f, 0) to make it empty and rewrite the file contents if necessary or unlink() the file if necessary.
  6. Close the file handle with fclose($f), which also releases the lock on it and lets another process continue with step 3.

The pattern is same for all programming languages.

share|improve this answer
The error appears randomly. If it was a requests problem, during 5000 hits it would appear for sure. Also simulating a flood from my pc, it doesn't produce the error. –  user2517612 Jun 24 '13 at 20:25
If you do not have much traffic (5000 hits is not much, unless it happens in a short time), you might see the error quite rarely, depending on your web server setup. You can reproduce it by generating parallel queries. Run multiple copies of a small script at the same time: while :; do curl -s -o /dev/null http://example.com/script.php; done –  oh7lzb Jun 24 '13 at 20:33
I will (try to) implement some file locking. I can't get intouch with the OP of the script, he could add it easily. –  user2517612 Jun 24 '13 at 20:37
I would appreciate someone to help me implement the lock needed in the code. I have failed so far. –  user2517612 Jun 25 '13 at 8:32
I added more exact steps with function parameter examples to implement the file access logic, I hope that helps. –  oh7lzb Jun 25 '13 at 9:50

Your Answer


