How to Implement the Rate Limiter Component in a Symfony 5 Project
Learn how to implement Symfony’s Rate Limiter component to secure your API by controlling request frequency, preventing abuse, and ensuring application stability with practical code examples and configuration tips.
APIs exposed to the public are often vulnerable to abuse through excessive or malicious requests. Fortunately, Symfony introduced a native Rate Limiter component starting in version 5.2. This powerful feature allows you to control how many requests users can make within a given time frame, helping prevent server overload and denial-of-service attacks.
In this guide, you'll learn how to set up Symfony's Rate Limiter, configure it for your application, and integrate it into your API using an event subscriber for clean and scalable implementation.
Requirements to Use Symfony's Rate Limiter
Before using the Rate Limiter component, your project must meet the following requirements:
- Symfony version 5.2 or higher
- PHP version 7.2.5 or higher
Ensure your project meets these versions before proceeding.
Installing the Rate Limiter Component
Start by installing the necessary Symfony package:
composer require symfony/rate-limiter
This command installs the rate-limiter
package along with its dependencies such as symfony/lock
and symfony/options-resolver
.
Configuring the Rate Limiter in Symfony
Create the Configuration File
Create a file named rate_limiter.yaml
in the config/packages
directory:
# config/packages/rate_limiter.yaml
framework:
rate_limiter:
anonymous_api:
policy: 'sliding_window'
limit: 5
interval: '1 minute'
Understanding the Configuration Options
- policy: Defines the rate limiting strategy. Learn more about each policy here. Supported values are:
fixed_window
sliding_window
token_bucket
- limit: Maximum number of requests allowed per interval
- interval: Time window in a PHP relative datetime format
You can also use the rate
object instead of interval
:
rate: { interval: '5 minutes', amount: 100 }
This means a user can make up to 100 requests every 5 minutes, but unused requests do not carry over to the next period.
Dealing with Semaphore Errors in PHP
If you are using a PHP version that has been compiled with the --enable-sysvsem
flag, you can directly go to the next section. But if it's not the case, you will have to make some more modifications.
If you encounter the following error:
Semaphore extension (sysvsem) is required.
It means your PHP version doesn't support sysvsem
. Instead of recompiling PHP, update your .env
file:
LOCK_DSN=flock
Changing LOCK_DSN
to flock
bypasses the semaphore dependency and resolves the issue quickly.
Creating a Test API in Symfony
To test the rate limiter, let's scaffold a basic API:
Generate a Controller
Run the Symfony CLI to generate a controller:
php bin/console make:controller MainController
Define API and Web Routes
Edit the generated controller:
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MainController extends AbstractController
{
/**
* @Route("/api", name="api_index")
*/
public function index(): Response
{
return $this->json([
'message' => 'Hello World!',
]);
}
/**
* @Route("/", name="app_home")
*/
public function home(): Response
{
return new Response("Hello world !");
}
}
The /api
route will simulate a basic API endpoint, while /
serves as a regular web page.
To test locally, run the Symfony server:
php -S localhost:8080 -t public
Visit http://localhost:8080/api to verify the JSON response.
{
"message": "Hello World!"
}
Centralizing Rate Limit Logic with an Event Subscriber
Instead of adding rate-limiting code to every controller method, create an EventSubscriber
to apply the logic globally to API routes.
Create the Subscriber
Create a new file at src/EventSubscriber/RateLimiterSubscriber.php
:
<?php
namespace App\EventSubscriber;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
class RateLimiterSubscriber implements EventSubscriberInterface
{
private RateLimiterFactory $anonymousApiLimiter;
public function __construct(RateLimiterFactory $anonymousApiLimiter)
{
$this->anonymousApiLimiter = $anonymousApiLimiter;
}
public static function getSubscribedEvents(): array
{
return [
RequestEvent::class => 'onKernelRequest',
];
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (strpos($request->get("_route"), 'api_') !== false) {
$limiter = $this->anonymousApiLimiter->create($request->getClientIp());
if (!$limiter->consume(1)->isAccepted()) {
throw new TooManyRequestsHttpException();
}
}
}
}
How It Works
- Listens to all HTTP requests
- Checks if the route name begins with
api_
- Applies the rate limiter using the client’s IP as an identifier
- Throws a
429 Too Many Requests
error if the limit is exceeded
Test the Rate Limiter
Open your browser and visit http://localhost:8080/api. Refresh more than 5 times within a minute and you’ll receive a 429 Too Many Requests
error — confirming the limiter works.
Conclusion
Implementing a rate limiter in your Symfony project is a crucial step toward securing your API and improving performance under load. By using Symfony’s native Rate Limiter component, you can control traffic, prevent abuse, and handle excessive requests gracefully.
Want to see the complete working example? Check out the source code here:
GitHub – MrAnyx/test-rate-limiter