ArtisanPack UI
DocsBookingsRate Limiting

Rate Limiting

Bookings v1.0.0

Every public route is reachable without credentials, so each one carries a bucket. The limits are named rather than numeric — rather than — and live...

Every public route is reachable without credentials, so each one carries a bucket. The limits are named rather than numeric — bookings.rate-limit:post rather than throttle:5,1 — and live under config( 'artisanpack.bookings.public.rate_limits' ), so an installation raises them for its own traffic in one place instead of at each route.

The buckets

Bucket Default (per minute) Keyed by Guards
post 5 address POST api/bookings, the widget, and the self-serve cancel / reschedule
read 60 address GET services, GET services/{slug}/providers, GET services/{slug}/slots
manage_get 20 address The manage page read
manage_token 60 manage token The manage read, the self-serve cancel / reschedule, and the customer feed
ical 30 address The provider and customer calendar feeds
ical_token 30 feed token The provider calendar feed

The public service, provider, and slot reads carry the read bucket because slot resolution is the most expensive read in the package — without it a script could walk a provider's whole calendar unbounded.

The routes that carry a link's whole credential are guarded twice — once per address, once per token — because the two bound different abuses: a machine grinding through guesses, and a link that has escaped into the world being hit from everywhere at once. The self-serve cancel and reschedule writes carry both the post (per address) and manage_token (per link) buckets, matching the manage read's stack.

Raising a limit

// config/artisanpack/bookings.php
'public' => [
    'rate_limits' => [
        'post'         => 20,
        'read'         => 120,
        'manage_get'   => 60,
        'manage_token' => 120,
        'ical'         => 60,
        'ical_token'   => 60,
    ],
],

Applying a bucket to your own route

The bookings.rate-limit middleware takes a bucket name, so the self-serve manage page you mount uses the same limiters the endpoint it fronts does:

Route::get( '/bookings/manage/{token}', /* … */ )
    ->middleware( [
        'bookings.rate-limit:manage_get',
        'bookings.rate-limit:manage_token',
        'bookings.manage-token',
    ] );

Declare the token limiter last so a guess is counted before it costs a lookup. See Self-Serve Management Page.

Behind a proxy

Every address-keyed bucket is only as truthful as Request::ip(). Behind a load balancer or CDN, that address is the proxy's unless you configure Laravel's trusted proxies — and then every customer shares one bucket. This is not optional for a public installation. See Trusted Proxies.

Stay in the Loop

Monthly tips, tutorials and package updates — plus a free Quick Start cheat sheet.

Subscribe
Edit this page on GitHub