Laravel Job Middleware: Rate Limiting &amp; Skip Patterns | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Laravel Job Middleware: Rate Limiting, Throttling, and Skipping Jobs Without Hacks

 Laravel Job Middleware: Rate Limiting, Throttling, and Skipping Jobs Without Hacks
===================================================================================

 Job middleware in Laravel lets you wrap queue job execution with reusable logic. Learn how to build rate-limited, throttleable, and conditionally skipped jobs cleanly — no static flags or queue hacks required.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 6 Sep 2026 · Updated 6 Sep 2026 · 4 min read

ShareCopy linkCopied

 ![Laravel Job Middleware: Rate Limiting, Throttling, and Skipping Jobs Without Hacks](https://cdn.msaied.com/635/e10d72c500d7a25f077552f3098478e8.png) 

  On this page +1. [Laravel Job Middleware: Rate Limiting, Throttling, and Skipping Jobs Without Hacks](#laravel-job-middleware-rate-limiting-throttling-and-skipping-jobs-without-hacks)
2. [What Job Middleware Actually Is](#what-job-middleware-actually-is)
3. [Built-In Rate Limiting with RateLimited](#built-in-rate-limiting-with-ratelimited)
4. [Throttling Bursts with WithoutOverlapping](#throttling-bursts-with-withoutoverlapping)
5. [Building a Custom Skip Middleware](#building-a-custom-skip-middleware)
6. [Composing Multiple Middleware](#composing-multiple-middleware)
7. [Testing Job Middleware in Isolation](#testing-job-middleware-in-isolation)
8. [Takeaways](#takeaways)

 Laravel Job Middleware: Rate Limiting, Throttling, and Skipping Jobs Without Hacks
----------------------------------------------------------------------------------

Laravel's job middleware feature is one of the most underused tools in the queue ecosystem. Most teams reach for static flags, database locks, or custom queue drivers when they need to control job execution flow. Job middleware gives you a cleaner, composable alternative — and it ships with the framework.

### What Job Middleware Actually Is

A job middleware is a plain PHP class with a single `handle(object $job, Closure $next)` method. You attach it via the `middleware()` method on your job class. Laravel calls each middleware in order before invoking `handle()` on the job itself.

```php
class EnsureUserIsActive
{
    public function handle(object $job, Closure $next): void
    {
        if (! $job->user->is_active) {
            $job->delete();
            return;
        }

        $next($job);
    }
}

```

Attach it to a job:

```php
public function middleware(): array
{
    return [new EnsureUserIsActive];
}

```

No traits, no base classes — just composition.

### Built-In Rate Limiting with RateLimited

Laravel ships `Illuminate\Queue\Middleware\RateLimited`, which integrates with the `RateLimiter` facade. Define a named limiter in `AppServiceProvider` (or a dedicated provider):

```php
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Cache\RateLimiting\Limit;

RateLimiter::for('stripe-webhooks', function (object $job) {
    return Limit::perMinute(30)->by($job->accountId);
});

```

Then apply it:

```php
use Illuminate\Queue\Middleware\RateLimited;

public function middleware(): array
{
    return [new RateLimited('stripe-webhooks')];
}

```

When the limit is exceeded, the job is automatically released back onto the queue with an exponential backoff. You can control the release delay:

```php
new RateLimited('stripe-webhooks')->dontRelease()
// or
new RateLimited('stripe-webhooks')->releaseAfterBackoff($this->attempts())

```

`releaseAfterBackoff` uses the job's attempt count to compute a sensible delay — critical for avoiding thundering-herd re-queues after a rate limit window resets.

### Throttling Bursts with WithoutOverlapping

`WithoutOverlapping` prevents concurrent execution of the same logical job:

```php
use Illuminate\Queue\Middleware\WithoutOverlapping;

public function middleware(): array
{
    return [
        (new WithoutOverlapping($this->userId))
            ->releaseAfter(30)
            ->expireAfter(180),
    ];
}

```

The key insight: `expireAfter` sets a TTL on the atomic lock so a crashed worker doesn't block the job forever. Always set it. The default is no expiry — a silent footgun in production.

### Building a Custom Skip Middleware

Sometimes you need to discard a job based on state that only exists at execution time — not dispatch time. A skip middleware handles this cleanly:

```php
class SkipIfAlreadyProcessed
{
    public function handle(object $job, Closure $next): void
    {
        $key = 'processed:' . $job->idempotencyKey;

        if (cache()->has($key)) {
            $job->delete();
            return;
        }

        $next($job);

        cache()->put($key, true, now()->addHours(24));
    }
}

```

This pattern is particularly useful for webhook processors and import jobs where the same payload can arrive multiple times.

### Composing Multiple Middleware

Middleware compose naturally — order matters:

```php
public function middleware(): array
{
    return [
        new SkipIfAlreadyProcessed,
        new RateLimited('external-api'),
        (new WithoutOverlapping($this->resourceId))->releaseAfter(10),
    ];
}

```

The skip check runs first, avoiding unnecessary lock acquisition and rate-limit counter increments for duplicate jobs.

### Testing Job Middleware in Isolation

Because middleware are plain classes, they're trivially testable with Pest:

```php
it('deletes the job when user is inactive', function () {
    $user = User::factory()->inactive()->make();
    $job = Mockery::mock(ProcessUserReport::class);
    $job->user = $user;
    $job->shouldReceive('delete')->once();

    $next = fn () => null;
    (new EnsureUserIsActive)->handle($job, $next);
});

```

No queue faking, no HTTP calls — pure unit test.

### Takeaways

- Job middleware is the correct abstraction for cross-cutting queue concerns — not base job classes or static state.
- Always set `expireAfter` on `WithoutOverlapping` to prevent permanent lock starvation after worker crashes.
- Use `releaseAfterBackoff` with `RateLimited` to avoid thundering-herd requeues at window reset.
- Skip middleware should call `$job->delete()` explicitly — releasing without deleting re-queues the job.
- Compose middleware in skip-first order to avoid wasting rate-limit budget on jobs that will be discarded anyway.
- Plain classes mean plain unit tests — no queue infrastructure required.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [queues](https://www.msaied.com/public/articles?search=queues)
- [job-middleware](https://www.msaied.com/public/articles?search=job-middleware)
- [performance](https://www.msaied.com/public/articles?search=performance)

 Frequently asked questions 
---------------------------

  Does job middleware work with batched and chained jobs?Yes. Middleware defined in a job's `middleware()` method applies regardless of whether the job is dispatched standalone, inside a batch, or as part of a chain. Each job in the batch or chain runs its own middleware independently.

   What happens to a rate-limited job's attempt count when it is released back to the queue?Releasing a job does not increment its attempt count. Only a failed execution increments `$this-&gt;attempts()`. This means a job can be released many times by rate-limiting middleware without burning through its `$tries` limit.

   Can I pass constructor arguments to job middleware dynamically from the job?Yes. Since middleware are instantiated inside the job's `middleware()` method, you have full access to `$this` and can pass any job property to the middleware constructor — for example, `new WithoutOverlapping($this-&gt;tenantId . ':' . $this-&gt;resourceId)`.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleFilament v3 Infolist Entries: Building Rich Read-Only Detail Pages Without Blade Sprawl](https://www.msaied.com/public/articles/filament-v3-infolist-entries-building-rich-read-only-detail-pages-without-blade-sprawl) [Next articleEloquent N+1 Elimination: Eager Loading Strategies and Query Deduplication at Scale](https://www.msaied.com/public/articles/eloquent-n1-elimination-eager-loading-strategies-and-query-deduplication-at-scale)  

   On this page
-------------

1. [Laravel Job Middleware: Rate Limiting, Throttling, and Skipping Jobs Without Hacks](#laravel-job-middleware-rate-limiting-throttling-and-skipping-jobs-without-hacks)
2. [What Job Middleware Actually Is](#what-job-middleware-actually-is)
3. [Built-In Rate Limiting with RateLimited](#built-in-rate-limiting-with-ratelimited)
4. [Throttling Bursts with WithoutOverlapping](#throttling-bursts-with-withoutoverlapping)
5. [Building a Custom Skip Middleware](#building-a-custom-skip-middleware)
6. [Composing Multiple Middleware](#composing-multiple-middleware)
7. [Testing Job Middleware in Isolation](#testing-job-middleware-in-isolation)
8. [Takeaways](#takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
