[tutorial] How to rate limit http requests in Laravel ?

· · 923 words · 5 minute read

Rate Limiting is the process of limiting the number of requests per minute sent to a web application is often necessary to protect against attacks trying to saturate your server or to brute force authentication forms.

So Laravel framework has a rate limiting mechanism, which we will use now to rate limit http requests per minute for all routes or some of them. You can rate limit for all users or per user.

create a custom rate limit 🔗

Start by creating a rate limit with your custom needs.

implement a rate limiter 🔗

Add this code in App\Providers\RouteServiceProvider.php file, inside the configureRateLimiting() function.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return Limit::perMinute(100);
});

change allowed number of attempts if the request from a user 🔗

If you want to change rate limit according to being a user or a guest. Change the custom rate limiter we code before to be like this.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return $request->user()
        ? Limit::perMinute(100)->by($request->user()->id)
        : Limit::perMinute(50)->by($request->ip());
});

After the change, the user will be allowed to make a hundred requests per minute only. But guest will be allowed to make a fifty requests per minute only. You can change the number as you like.

give a custom response after reaching the rate limit 🔗

If you want to return a custom response when the user/guest reaches their rate limit, just add Response to the code of rate limiter. Check the following code for inspiration.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return $request->user()
        ? Limit::perMinute(100)->by($request->user()->id)->response(function () {
            return new Response('Our Server Is Busy, Please Wait!');
        })
        : Limit::perMinute(50)->by($request->ip())->response(function () {
            return new Response('Our Server Is Busy, Please Wait!');
        });
});

You can specify the status code of the http response after reaching the rate limit.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return $request->user()
        ? Limit::perMinute(100)->by($request->user()->id)->response(function () {
            return new Response('Our Server Is Busy, Please Wait!', 429);
        })
        : Limit::perMinute(50)->by($request->ip())->response(function () {
            return new Response('Our Server Is Busy, Please Wait!', 429);
        });
});

the status code 429 means too many requests. You can use any http response status code as you like.

You can check all http response status codes on Mozilla Developer Network (MDN) .

And if you want to pass the http headers, add them like this.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return $request->user()
        ? Limit::perMinute(100)->by($request->user()->id)->response(function (array $headers) {
            return new Response('Our Server Is Busy, Please Wait!', 429, $headers);
        })
        : Limit::perMinute(50)->by($request->ip())->response(function (array $headers) {
            return new Response('Our Server Is Busy, Please Wait!', 429, $headers);
        });
});

rate limit guests only 🔗

if you want to rate limit people who are not logged in, just add Limit::none() if the request came from a logged-in user. Code it like this.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return $request->user()
        ? Limit::none()
        : Limit::perMinute(50)->by($request->ip())->response(function (array $headers) {
            return new Response('Our Server Is Busy, Please Wait!', 429, $headers);
        });
});

You can change the rate limiting of guests as you like by changing these lines of code from the custom rate limiter.

Limit::perMinute(50)->by($request->ip())->response(function (array $headers) {
    return new Response('Our Server Is Busy, Please Wait!', 429, $headers);
});

You can remove the custom http response like this.

Limit::perMinute(50)->by($request->ip());

And of course, you can change the allowed number of requests to any number you like.

applying the custom rate limit 🔗

Your custom rate limit will not be applied automatically. You need to specify the routes you want to rate limit. And if you want to rate limit all http requests on all routes, you can.

apply the rate limiter you just created on all routes 🔗

Add the rate limiter to all web routes by adding 'throttle:customRateLimiter' as a middleware in App\Providers\RouteServiceProvider.php like this.

public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));

        Route::middleware(['web', 'throttle:customRateLimiter'])
            ->group(base_path('routes/web.php'));
    });
}

and voila, all http requests to all routes will be rate limited as you wish.

apply the rate limiter you just created on specific routes 🔗

Until now all of the above code is not used. You should rate limit all of the routes you want by adding middleware('throttle:customRateLimiter') to them in routes\web.php, like this.

Route::get('/main', function () {
    return view('main');
})->name('main')->middleware(['throttle:customRateLimiter']);

or like this code.

Route::group(['middleware' => ['\App\Http\Middleware\CustomCKFinderAuth', 'throttle:customRateLimiter']], function () {
    Route::any('/ckfinder/connector', 'CKSource\CKFinderBridge\Controller\CKFinderController@requestAction')->name('ckfinder_connector');
    Route::any('/ckfinder/browser', 'CKSource\CKFinderBridge\Controller\CKFinderController@browserAction')->name('ckfinder_browser');
});

or like this code.

Route::post('/stores/verify/{id}', [StoreController::class, 'verify'])->middleware(['throttle:customRateLimiter'])->name('stores.verify');

rate limit all requests received by the website from all users 🔗

We were rate limiting requests per user per minute. But if you would like to rate limit all requests from all users per minute, just remove the ->by() code snippet from the custom rate limiter we created above.

You can make the rate limiter to be like this.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return Limit::perMinute(1000)->response(function () {
            return new Response('Our Server Is Busy, Please Wait!');
        });
});

This code limits the total http requests received by the website per minute to be a thousand.

You can remove the custom response shown when rate limit reached.

RateLimiter::for('customRateLimiter', function (Request $request) {
    return Limit::perMinute(1000);
});

Add the rate limiter to all web routes by adding 'throttle:customRateLimiter' as a middleware in App\Providers\RouteServiceProvider.php file to be like this.

public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));

        Route::middleware(['web', 'throttle:customRateLimiter'])
            ->group(base_path('routes/web.php'));
    });
}

and voila, all http requests to all routes received by the web app will be rate limited as you wish. So your website is protected against DoS and DDoS.

I hope this helps.

Follow me to get notified of new posts I create here .

Share: