CodeBlanks Logo Icon CodeBlanks

Rate limiting

PHP course · Expert · Lesson 30/54

Learn to prevent brute force by applying rate limiting on login and API endpoints.

What is rate limiting for?

Rate limiting caps how many requests a user can make in a given window. It is used against brute force on login forms, scraping on API endpoints and DDoS. The fixed window is the simplest method: a counter per minute resets at the boundary; requests over the limit are rejected.

The sliding window is finer: it counts timestamps in the last N seconds. In production you store counters in APCu or Redis; in distributed systems you add jitter with random_int. When the limit is exceeded, return 429 Too Many Requests with a Retry-After header.

Fixed window vs sliding window

The fixed window resets the counter at the start of each minute; it can double at the window boundary. The sliding window keeps timestamps from the last 60 seconds; it is finer but uses more memory. In production the most efficient fixed window is built with Redis INCR and EXPIRE.

When the limit is hit, return 429; the Retry-After header tells the wait in seconds. To avoid thundering herd in a distributed setup, each server waits random_int(0, $jitter) milliseconds. Always count server side; a client side counter is unreliable.

Exercise Write an isRateLimited function

Define isRateLimited(array $attempts, int $limit = 3): bool. Return true when count($attempts) > $limit, false otherwise. Define $attempts1 = [100, 110, 120, 130, 140] and $attempts2 = [100, 110]. Call isRateLimited for each array; print the result as 1 or 0.

Show example solution
bool { return count($attempts) > $limit; }. $attempts1 = [100, 110, 120, 130, 140]; (5 items > 3 = true = 1), $attempts2 = [100, 110]; (2 items > 3 = false = 0). Print 1/0 with a ternary.
Example solution:
<?php
function isRateLimited(array $attempts, int $limit = 3): bool {
    return count($attempts) > $limit;
}
$attempts1 = [100, 110, 120, 130, 140];
$attempts2 = [100, 110];
echo (isRateLimited($attempts1) ? "1" : "0") . "\n";
echo (isRateLimited($attempts2) ? "1" : "0") . "\n";

Test yourself

  1. Complete the random_int jitter call

    $jitter = ____(100, 500);
    Show answer
    $jitter = random_int(100, 500);
  2. Which HTTP code should be returned when the limit is exceeded?

    • 200 OK
    • 401 Unauthorized
    • 403 Forbidden
    • 429 Too Many Requests
    Show answer

    429 Too Many Requests — Correct. 429 Too Many Requests signals the limit was exceeded.

  3. Match the rate limiting concepts

    fixed_window
    Resets each minute
    sliding_window
    Counts last N seconds
    429
    Too Many Requests
    random_int
    Secure random for jitter

All lessons in Expert