Laravel — P53: Rate Limiting

Throttle traffic, keep your API safe

For the sharp-eyed readers, you might have noticed a method called configureRateLimiting in the RouteServiceProvider in our previous article.

protected function configureRateLimiting()
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });
}

What this does is limits the user’s requests per minute. The default is set to 60 requests per minute. If you needed to modify the number of requests, this is where you would typically do it (i.e. change it from 60 to 100).

As you can see in the code, the rate limiter’s are defined in the RateLimiter facade’s for method. It accepts a rate limiter and a closure that returns the limit configuration. In this instance, this is a rate limiter for the api routes, which we haven’t covered yet.

What if we wanted to have a web rate limiter? We can create a new RateLimiter and change the api to web.

protected function configureRateLimiting()
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });

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

If the limit is exceeded, a 429 error is sent back to the user. For the sake of testing, I’ve change the rate limiting to 5 and refreshed the page so you can see the response.

If we refresh the page more than 5 times, nothing happens. What’s happening? We need to apply the throttle middleware to the routes that we want throttled.

Route::middleware(['throttle:web'])->group(function () {
    Route::get('/throttle-test-1', function() {
        return "Throttle Test 1";
    });

    Route::get('/throttle-test-2', function() {
        return "Throttle Test 2";
    });
});

We haven’t covered middleware yet, but we will soon enough. In this example, you just need to pass the throttle: followed by the name of the rate limiter to the middleware method. After the 5th attempt, we get the following message.

 

For those that are jumping ahead and check the api.php routes file, you’ll be surprised to find that the throttle:api does not occur there.

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

There is an auth:sanctum middleware, but there is no throttle:api. So, how does the api route file work? I’m glad you asked (not really). Open app\Http\Kernel.php.

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array<int, class-string|string>
     */
    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array<string, array<int, class-string|string>>
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array<string, class-string|string>
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \App\Http\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];
}

This file contains all of the various different middleware definitions and when they’re applied. If you start scanning down the page, you’ll find the following protected property.

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

Under the api key, you can see throttle:api. If we wanted to apply the throttle:web rate limiter that we just defined, we could add it to our web array.

protected $middlewareGroups = [
    'web' => [
        'throttle:web',
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

Now, your web rate limiter is applied to all web routes. Refresh any route 5 times and you’ll see that it’s going to return a 429 error.

I will comment this out of the Kernel.php file and change the web rate limiter to 100. You can define as many rate limiters as you would like and apply them to whatever routes you choose. And don’t worry too much about middleware and the Kernel file; we’ll go over each of those in later articles.

Laravel Series

Continue your Laravel Learning.

Laravel — P52: Explicit Route Model Binding

Take full control of model resolution in your routes

Laravel – P52: Route Model Explicit Binding

In part 52 of our Laravel series, explore explicit route‑model binding to gain precise control over how route parameters resolve to Eloquent models. Learn custom key selection, fallback handling, and advanced security for clean, predictable routing.

Laravel — P53: Rate Limiting

Throttle traffic, keep your API safe

Laravel – P53: Rate Limiting

In part 53 of our Laravel series, learn to throttle traffic with customizable rate limits. Explore global and per‑route strategies, dynamic keys, back‑off responses, and API protection techniques to keep your application stable under load.

Laravel — P54: Route Groups — Middleware and Controllers

Organize routes, secure them, and keep controllers lean

Laravel – P54: Route Groups Middleware & Controllers

In part 54 of our Laravel series, unlock the power of route groups to apply middleware stacks and shared controller namespaces in one stroke. Minimize repetition, harden security, and keep your routes file tidy as your API grows.

Leave a Reply