Learn how to build a rate limiter in PHP using Redis. Protect your application from abuse by limiting requests per user with INCR
, EXPIRE
, and sliding window techniques.
Introduction
Rate limiting is essential to prevent abuse, bot attacks, and excessive API requests. By implementing Redis-based rate limiting, you can:
- Restrict API calls per user/IP within a time window.
- Throttle abusive traffic without affecting legitimate users.
- Ensure fair resource allocation across all users.
Redis provides fast in-memory counting, making it ideal for rate limiting with commands like:
INCR
– Increments request count.EXPIRE
– Sets an expiration for the request counter.ZADD
/ZREMRANGEBYSCORE
– Implements a sliding window rate limiter.
This guide covers:
- Implementing fixed window and sliding window rate limiting in PHP
- Using PHPRedis and Predis for Redis-based throttling
- Best practices for scalable rate limiting
1. Why Use Redis for Rate Limiting?
Traditional PHP-based rate limiting methods use:
❌ Session-based limits – Inefficient for APIs and distributed systems.
❌ Database-based limits – Slow and causes performance bottlenecks.
Redis is fast and scalable for rate limiting because:
✅ Stores counters in-memory, reducing lookup time.
✅ Automatically expires old counters, freeing memory.
✅ Works well in distributed systems.
2. Implementing Fixed Window Rate Limiting with Redis
A fixed window approach limits requests within a defined time window.
Step 1: Connect to Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
Step 2: Define Rate Limit Parameters
$rateLimit = 10; // Maximum 10 requests
$timeWindow = 60; // Time window (60 seconds)
$ip = $_SERVER['REMOTE_ADDR']; // User IP
$cacheKey = "rate_limit:$ip";
Step 3: Check Request Count
$requests = $redis->incr($cacheKey);
if ($requests == 1) {
$redis->expire($cacheKey, $timeWindow);
}
if ($requests > $rateLimit) {
http_response_code(429);
die("Rate limit exceeded. Try again later.");
}
echo "Request allowed. Current count: $requests";
How It Works:
- Each request increments the counter.
- The first request sets an expiration on the counter.
- If requests exceed the limit, return HTTP 429 (Too Many Requests).
3. Implementing Sliding Window Rate Limiting
A sliding window approach ensures more even distribution of requests over time.
Using ZADD
and ZREMRANGEBYSCORE
for Time-Based Sliding Window
Instead of resetting at a fixed time, Redis tracks individual request timestamps and removes old ones.
$limit = 10;
$timeWindow = 60;
$ip = $_SERVER['REMOTE_ADDR'];
$cacheKey = "rate_limit:$ip";
$currentTime = microtime(true) * 1000; // Current time in milliseconds
// Remove outdated requests
$redis->zremrangebyscore($cacheKey, 0, $currentTime - ($timeWindow * 1000));
// Get current request count
$requestCount = $redis->zcard($cacheKey);
if ($requestCount >= $limit) {
http_response_code(429);
die("Rate limit exceeded.");
}
// Add the new request timestamp
$redis->zadd($cacheKey, $currentTime, $currentTime);
// Set expiration to auto-cleanup old data
$redis->expire($cacheKey, $timeWindow);
echo "Request allowed. Total requests in window: " . ($requestCount + 1);
How It Works:
- Each request logs its timestamp in a Redis sorted set.
- Expired timestamps are removed before checking the request count.
- Only valid requests within the last X seconds are counted.
4. Rate Limiting API Endpoints
Middleware for Rate Limiting API Calls
function rateLimitMiddleware($limit, $timeWindow) {
global $redis;
$ip = $_SERVER['REMOTE_ADDR'];
$cacheKey = "rate_limit:$ip";
$requests = $redis->incr($cacheKey);
if ($requests == 1) {
$redis->expire($cacheKey, $timeWindow);
}
if ($requests > $limit) {
http_response_code(429);
die(json_encode(["error" => "Rate limit exceeded"]));
}
}
// Apply rate limiter (5 requests per 30 seconds)
rateLimitMiddleware(5, 30);
echo json_encode(["message" => "API request successful"]);
5. Resetting Rate Limits Dynamically
Manually Reset User’s Rate Limit
$redis->del("rate_limit:$ip"); // Clears the rate limit for the user
Setting Different Limits for Different Users
$userType = "premium"; // Assume a user type
$rateLimits = ["free" => 10, "premium" => 50];
$limit = $rateLimits[$userType] ?? 10;
- Free users get 10 requests per minute.
- Premium users get 50 requests per minute.
6. Best Practices for Redis Rate Limiting
✅ Use fixed windows for simple rate limits (e.g., login attempts).
✅ Use sliding windows for API requests to ensure fairness.
✅ Set appropriate expiration (EXPIRE
) to free memory.
✅ Adjust rate limits dynamically for different users or services.
✅ Monitor Redis performance to track memory usage.
Conclusion
Redis-powered rate limiting ensures fair API usage, prevents abuse, and improves security.
This guide covered:
- Fixed and sliding window rate limiting
- Using
INCR
,EXPIRE
,ZADD
, andZREMRANGEBYSCORE
- Implementing rate limits in PHP APIs
By following these methods, you can protect your application while maintaining performance and scalability.