27

I'm writing a lot of API to get and store data.
I like the default throttle option:

protected $middlewareGroups = [
    'api' => [
        'throttle:60,1',
        'bindings',
    ],
];

to limit the request to 60 per minute; but for some route (es: POST), I'd like to increase this value.

I tried to set 'throttle:500,1' on route Middleware like below:

Route::group(function () {
        Route::get('semaphore/1',        ['uses' => 'App\Api\V1\DBs\SemaphoreController@index']);
        Route::post('semaphore/1',       ['uses' => 'App\Api\V1\DBs\SemaphoreController@store',        'middleware' => 'WriteToDatabaseMiddleware', 'throttle:500,1']);
});

but it does not work.

Any idea?

Thank you.

UPDATE:
I noticed that the 'throttle:500,1' used in the api.php route will be set AFTER the default 'throttle:60,1' specified into Kernel.php file; then, It doesn't work.

Logging the process execution, the first call is:

Illuminate\Routing\Middleware\ThrottleRequests -> handle

from Kernel.php has maxAttempts=60.

Then, the second call is:

Illuminate\Routing\Middleware\ThrottleRequests -> handle

from api.php has maxAttempts=500.

In others words, the throttle:500,1 in the api.php file do not override the throttle:60,1 in the Kernel.php file.

vlauciani
  • 1,010
  • 2
  • 13
  • 27

3 Answers3

21

Current answer

According to this GitHub issue, the throttle middleware should not be used "twice" (like you want to do that). There are only two ways how to deal with your current problem "correctly":

  1. Write an own throttling middleware

or

  1. Define the throttle middleware separately for each route (group)

Old answer

You set the middleware key wrong! When declaring multiple middleware to use, create a new array for them

['middleware' => ['WriteToDatabaseMiddleware','throttle:500,1']]

EDIT: Because of the middleware order, you should set your kernel throttle to the highest value you want to use, and all other routes that should have a lower throttle value to the corresponding ones.

manniL
  • 7,157
  • 7
  • 46
  • 72
  • You are welcome @vlauciani It'd be great if you'd accept the answer because it solved your problem :) – manniL May 27 '17 at 07:56
  • I test on my code but It doesn't work; I've update my Question with the investigation. – vlauciani May 31 '17 at 12:40
  • @vlauciani Well, then you should change your Kernel values to `throttle:500,1` (to the higher one), and all other routes that should have throttle to `throttle:60,1`. This should work with the request order. – manniL May 31 '17 at 12:49
  • yes, your solution is my idea... the problem is that with this approach all route will be set to `500`; the I've to remember to set to `60` every new route. I prefer to have a standard value (ex: `60`) and override to `500` only specific route. – vlauciani May 31 '17 at 12:54
  • @vlauciani I see your point. Anyway, because it looks like the problem is on the Laravel site, better create an issue on their GitHub repo. I can't do more here :| – manniL May 31 '17 at 13:30
  • @vlauciani Update – manniL Jun 02 '17 at 07:44
  • 1
    Thank you @manniL, I read you "**Current answer**" now. Ok, It clarifies the operation; I think the solution is to create my own throttling middleware. Thank you again. – vlauciani Jun 02 '17 at 17:26
  • You cannot use independent throttles on different groups in Laravel 5.x: https://github.com/laravel/framework/pull/28856 – Chuck Le Butt Sep 01 '21 at 18:44
3

In laravel 6 you can use prefix for prevent with global throttle. use 'throttle:5,1,prefix'

Route::group(['prefix' => 'contact-us', 'middleware' => 'throttle:5,1,contact-form',], function () {
    Route::post('/', 'ContactUsController@store');
});

Allow multiple throttles by naming

Ali Yousefi
  • 472
  • 4
  • 11
  • 1
    Laravel 5.9? Do you mean Laravel 6. After 5.8 Laravel went to semantic versioning starting at version 6.0.0. – Andrew Bibby Feb 05 '20 at 11:02
  • Hi Andrew, version 5.9 is wrong and fix my answer. if you want use prefix in throttle middleware, this pull request may help you [Allow multiple throttles by naming](https://github.com/laravel/framework/pull/28856). – Ali Yousefi Feb 07 '20 at 14:51
1

None of the current answers explain Laravel 5.x behaviour. In that version every instance of "throttle" uses the same bucket. So if you place a throttle command in two separate locations it affects every instance.

Consider:

// User can email 5 times an hour
Route::post('/email', 'Ctrl@email')->middleware('throttle:5,60');
// User can search 100 times an hour
Route::get('/search', 'Ctrl@search')->middleware('throttle:100,60);

If a user searches 5 times in a 5 minute period, they will not able to email in the next hour.

In Laravel 5.x there is no way around this. In Laravel 6 onwards they added the ability to name throttles, giving them separate buckets.

Chuck Le Butt
  • 47,570
  • 62
  • 203
  • 289