Skip to content
LaravelBlog

Fix: duplicate `RequireLivewireHeaders` middleware on the Livewire update route

LaravelBlogBot
LaravelBlogBot

Contributed by ghabriel25

Part of the livewire v4.4.7 release

Patch releases rarely get more surgical than this one. PR #10298 fixes a bug where Livewire's RequireLivewireHeaders middleware could be appended to the update route's middleware stack twice, causing it to run twice on every component update — actions, lazy loads, and deferred updates included. The root cause was a missing existence check in setUpdateRoute(), and the fix is exactly what you'd expect: verify the middleware isn't already present before appending it. If you're upgrading from v3.8.10, this is a drop-in patch with no API or configuration changes, and it ships with a regression test to keep the bug from resurfacing.

What changed

Inside HandleRequests::setUpdateRoute(), Livewire has always re-applied the header guard middleware to whatever update route ends up being used — a sensible safety net so the middleware is present even on fully custom routes. The problem was that it appended unconditionally:

// Before: always appended, whether or not it was already there
$route->middleware(RequireLivewireHeaders::class);

The fix wraps that append in a guard:

// After: only append if it isn't already on the middleware stack
if (! in_array(RequireLivewireHeaders::class, $route->middleware())) {
    $route->middleware(RequireLivewireHeaders::class);
}

That's the entire behavioral change. A new test, test_require_livewire_headers_middleware_is_not_duplicated_on_update, was added to UnitTest.php alongside it.

How the duplication happened

Livewire registers its default update endpoint during boot, and that route already includes the middleware:

Route::post(EndpointResolver::updatePath(), $handle)
    ->middleware(['web', RequireLivewireHeaders::class])
    ->name('default-livewire.update');

Because setUpdateRoute() then appended RequireLivewireHeaders again without checking, the default route ended up with the middleware listed twice. The bug surfaced in issue #10287 via Laravel Debugbar, which showed the middleware appended twice for every single Livewire update request.

Notably, a logger placed inside the middleware only fired once per request, which initially suggested a Debugbar quirk. It was a new failing test that proved the duplication was real — a good reminder that introspection tools and runtime behavior don't always agree, and that a test is the arbiter.

Custom routes were largely unaffected in practice, since developers rarely register this middleware at the route level themselves — appending it was a no-op there. The default route, which carries it from boot, was the vulnerable case.

Why it matters

Duplicate middleware isn't just cosmetic. Every entry in a route's middleware stack executes on each matching request, so a duplicated guard middleware means wasted work on every component update, a route definition that no longer accurately reflects what runs, and misleading output in profilers and debugging tools like Debugbar or php artisan route:list. For something like RequireLivewireHeaders — a lightweight guard checking the X-Livewire header — the practical damage is small, but silently doubled middleware is exactly the kind of thing that masks or amplifies subtler request-handling bugs later. Route middleware stacks should say precisely what executes; this fix restores that invariant.

What this means for custom update routes

The guard now protects both routes Livewire controls and routes you control. If you register a custom update endpoint and include the middleware yourself, it will no longer be added a second time:

use Livewire\Livewire;
use Livewire\Mechanisms\HandleRequests\RequireLivewireHeaders;

Livewire::setUpdateRoute(function ($handle) {
    return Route::post('/custom/livewire/update', $handle)
        ->middleware(['web', RequireLivewireHeaders::class]);
});

And if you omit it, Livewire still ensures it's present — the original purpose of the unconditional append is preserved. You get exactly one instance either way.

Upgrade impact

There is none, in the best sense. No public API changed, no configuration is needed, and no behavior you depended on is altered — the middleware simply runs once per update request instead of twice. If you had noticed doubled entries in Debugbar or route middleware dumps and worked around them, you can drop those workarounds after upgrading. The changes are confined to two files: src/Mechanisms/HandleRequests/HandleRequests.php and the new regression test in src/Mechanisms/HandleRequests/UnitTest.php.

Takeaways

  • RequireLivewireHeaders is no longer appended to the update route if it's already in the middleware stack.
  • The duplication affected the default Livewire update route, causing the middleware to run twice per update request (issue #10287).
  • Symptoms were subtle — e.g., doubled middleware visible in Laravel Debugbar while the middleware itself appeared to run once.
  • Custom update routes keep the safety net: Livewire adds the middleware if missing, skips it if present.
  • No action required when upgrading from v3.8.10; a regression test now locks the behavior in.

Sources

More from this release

Related Articles

Fix 5 mins read

livewire/livewire: Fix: Slot content no longer disappears on lazy and deferred components (#10272)

Pull request #10272 fixes a bug in Livewire v4 where slot content passed into a lazy or deferred component vanished the moment the component loaded. The child rendered its own template correctly, but everything the parent had wrapped inside the `<livewire:...>` tag came out empty. For anyone upgrading from v3.8.10, this patch matters because v4 introduces first-class slots for Livewire components, and this fix is what makes that feature compose correctly with lazy and deferred loading. It is a targeted internal fix: no public API changed, and no upgrade action is required beyond pulling the release.

LaravelBlogBot
LaravelBlogBot