Use Redis® as a Rate Limiter with PHP on Ubuntu 20.04
Introduction
Rate limiting is a method of limiting resource usage in your server. For instance, if you're running an API (Application Programming Interface), you may put a cap limiting the number of requests a user can make on your server within a specific timeframe. Controlling the rate of requests made in your web application reduces the risk of DoS (Denial of Service) attack. This allows you to enforce a fair usage policy for your application. In a large-scale web application, limiting operations from exceeding certain constraints also allows you to save huge costs on bandwidth and resource usage.
This guide uses Redis® server to limit requests to your web resource with PHP on a Vultr Ubuntu 20.04 server.
Prerequisites
To follow along with this guide, ensure you have the following:
- An Ubuntu 20.04 server.
- A sudo user.
- A Lamp Stack.
- A Redis® Server
1. Install Redis® Extension
Log in to your Vultr Ubuntu cloud server and install the php-redis
extension. This module allows you to use the Redis® Library in PHP code.
$ sudo apt install -y php-redis
Restart the Apache server to load the new configuration.
$ sudo systemctl restart apache2
2. Create a PHP Resource
Create a PHP resource in the root directory of your server.
$ sudo nano /var/www/html/resource.php
Then, add the information below to the file.
<?php
try {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$username = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
$key = $username;
$allowed_requests_per_period = 3;
$time_frame_in_seconds = 10;
$total_requests = 0;
if ($redis->exists($key)) {
$redis->INCR($key);
$total_requests = $redis->get($key);
if ($total_requests > $allowed_requests_per_period) {
echo "Hey, " . $username . ", You've already reached your requests limit. Total requests "
. $total_requests . " Allowed requests " . $allowed_requests_per_period;
exit();
}
} else {
$redis->set($key, 1);
$redis->expire($key, $time_frame_in_seconds);
$total_requests = 1;
}
echo "ok, total requests " . $total_requests;
} catch (Exception $e) {
echo $e->getMessage();
}
?>
When you finish editing the file, save and close it by pressing Ctrl + X, Y then Enter.
The /var/www/html/resource.php
code explained:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
You've used the above code to connect to a Redis® server on your localhost via port 6379.
$username = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
When you've configured PHP to run as an Apache module, the $_SERVER['PHP_AUTH_USER']
and $_SERVER['PHP_AUTH_PW']
variables assign the HTTP Basic Authentication credentials to the $username
and $password
variables respectively.
$key = $username;
Because you might have many users accessing your web resource, creating a Redis® key with the $username
variable retrieved from the $_SERVER['PHP_AUTH_USER']
variable allows you to track the traffic of each user independently.
$allowed_requests_per_period = 3;
$time_frame_in_seconds = 10;
The two variables above allow you to limit your web resource requests depending on the set usage policy in your application. This example limits users to 3 requests in 10 seconds. Adjust these values depending on your needs.
$total_requests = 0;
You've initialized the total requests made by a user using the $total_requests
variable.
if ($redis->exists($key)) {
$redis->INCR($key);
$total_requests = $redis->get($key);
if ($total_requests > $allowed_requests_per_period) {
echo "You've already reached your requests limit. Total requests "
. $total_requests . " Allowed requests " . $allowed_requests_per_period;
exit();
}
} else {
$redis->set($key, 1);
$redis->expire($key, $time_frame_in_seconds);
$total_requests = 1;
}
The code above checks for the existence of the $key
variable on the Redis® server. If the key exists, you are incrementing it by 1
using the $redis->INCR($key)
command. If the key does not exist, you are setting a new key with a default value of 1
using the $redis->set($key, 1)
command as well as the expiration period defined by the $time_frame_in_seconds
variable. Next, you are accessing the current count of the $key
key using the $redis->get($key)
. Once the value is retrieved, you're assigning it to the $total_requests
variable.
Then, you're using the PHP logic if...else
syntax to evaluate if the client has exceeded the total requests allowed during the period defined. If the limit is exceeded, you're raising an error to the user.
echo "ok, total requests " . $total_requests;
Towards the bottom of the file, you're echoing the number of requests made by the user if they've not exceeded their limit.
3. Test the Redis® Rate Limiter
Open a command prompt on your server and use the curl
command to query the /var/www/html/resource.php
web resource that you have created above. The dummy query string ?[1-20]
at the end of the http://localhost/resource.php
URL allows you to access the resource 20 times to see if the script will be rate-limited by the Redis® server.
$ curl --user john:SAMPLE_PASSWORD
-H "Accept: text/plain"
-H "Content-Type: text/plain"
-X GET http://localhost/resource.php?[1-20]
After running the above code, you should get the below output.
[1/20]: http://localhost/resource.php?1 --> <stdout>
--_curl_--http://localhost/resource.php?1
ok, total requests 1
[2/20]: http://localhost/resource.php?2 --> <stdout>
--_curl_--http://localhost/resource.php?2
ok, total requests 2
[3/20]: http://localhost/resource.php?3 --> <stdout>
--_curl_--http://localhost/resource.php?3
ok, total requests 3
[4/20]: http://localhost/resource.php?4 --> <stdout>
--_curl_--http://localhost/resource.php?4
Hey, john, You've already reached your requests limit. Total requests 4 Allowed requests 3
[5/20]: http://localhost/resource.php?5 --> <stdout>
--_curl_--http://localhost/resource.php?5
Hey, john, You've already reached your requests limit. Total requests 5 Allowed requests 3
[6/20]: http://localhost/resource.php?6 --> <stdout>
...
Notice that the first three requests are accepted. However, the PHP script started throwing a rate-limit error beginning at the fourth request.
Conclusion
In this tutorial, you've used a Redis® server to limit resource usage with PHP on Ubuntu 20.04. While this guide is just an example of how rate limiting works with Redis®, you can extend it further to suit your web application's needs.