Skip to content
LaravelBlog

Fix fetch responses with a http redirect being parsed as Livewire payloads

LaravelBlogBot
LaravelBlogBot

Contributed by joshhanley

Part of the livewire v4.4.7 release

If you have ever logged a user out while a Livewire component was on screen and seen a flash of the error modal or a JSON parse error scroll past in the console, this release is for you. PR #10369 fixes how the JavaScript request pipeline handles plain HTTP redirects on Livewire update requests. Previously, when the server answered an update request with a 302 — say, an auth middleware bouncing an expired session to /login — the browser's fetch() followed the redirect and handed Livewire the HTML of the target page. Livewire had already started navigating away, but the pipeline kept running and tried to parse that HTML as a Livewire payload, producing a cosmetic-but-alarming failure right before the page changed. The fix cleanly ends the request as soon as a redirected response is detected.

The scenario this fixes

Livewire talks to the server through fetch() calls to the update endpoint. The fetch() API follows HTTP redirects transparently: when the server replies with a 302, the browser silently fetches the redirect target and returns the final response to Livewire — typically the HTML of a login page — with response.redirected set to true and response.url pointing at the final destination.

This path is hit whenever middleware or the framework responds to a Livewire update with a normal redirect instead of a Livewire JSON response:

public function handle($request, Closure $next)
{
    // E.g. auth middleware intercepting an expired-session update request
    if ($request->hasHeader('X-Livewire') && ! auth()->check()) {
        return redirect('/login');
    }

    return $next($request);
}

Any component action that triggers an update request while the session is expired would land here. Note that this is different from component-level redirects like $this->redirect('/dashboard'), which are delivered inside the JSON payload and were never affected.

What changed under the hood

Livewire already anticipated redirects in sendRequest(): it checks response.redirected and calls the redirect handler, which sets window.location.href to the final URL. The bug was what happened next — execution simply fell through to dump detection and JSON parsing:

// Before: the redirect fired, but processing continued...
if (response.redirected) {
    handlers.redirect(response.url)
}

// ...so dump detection and JSON.parse(responseBody) still ran on the HTML

Because the response body at that point is the redirected page's HTML, JSON.parse() throws, and Livewire's fallback error handling kicks in — the modal flashes, console errors appear — even though a top-level navigation is already underway.

The fix stops the pipeline right after the redirect handler runs:

// After: redirect fires and the request lifecycle ends cleanly
if (response.redirected) {
    handlers.redirect(response.url)
    handlers.finish()

    return
}

Once a redirected fetch response is detected, handlers.finish() completes the request and the function returns immediately. The redirected HTML never reaches dump detection, JSON.parse(), or the error modal fallback.

Why it matters

On its own, this bug was harmless in the sense that the navigation eventually happened anyway. But the symptoms undermined confidence in the framework: users saw the error modal for a split second during a perfectly normal expired-session redirect, and developers chasing console noise could easily mistake the parse error for a real failure. Apps that protect their Livewire endpoints with middleware — which is to say, most apps with authentication — were exposed to this on every logout or session expiry that raced with a pending action.

New test coverage

The PR adds a browser test in Features/SupportRedirects/BrowserTest.php that pins the behavior down. A component "arms" a session flag, a registered middleware then redirects the next Livewire update request to a plain HTML page, and the test asserts that the browser lands on the redirected URL, sees the HTML content, and — crucially — that assertConsoleLogHasNoErrors() passes. That last assertion is the regression guard: without the fix, the pipeline would log JSON parse errors before the navigation completes.

Upgrade impact from v3.8.10

The upgrade impact is minimal, and entirely positive:

  • There are no public API, configuration, or template changes. The diff touches only the JavaScript request pipeline plus test files.
  • No behavior you could have reasonably relied on changes. The redirect itself worked before and still works; what changes is that the spurious error handling after the redirect no longer runs.
  • Since the PR targets 4.x, you pick it up automatically as part of your upgrade from v3.8.10 — there is nothing extra to enable or migrate.

If anything, your app gets quieter: redirected update requests now end cleanly, with no error modal flash and no console noise.

Takeaways

  • Redirected fetch responses (302s on the update endpoint) are now treated as the end of the Livewire request; the redirected HTML body is never parsed as a payload.
  • Component-level redirects via redirect() / $this->redirect() are unaffected — they travel inside the JSON payload and always did.
  • No code changes are required when upgrading from v3.8.10; the fix applies automatically in 4.x.
  • A new browser test guards against console errors whenever a Livewire update is answered with an HTTP redirect to an HTML page.

Sources

More from this release

Related Articles

Fix 2 mins read

livewire/livewire: [4.x] Skip Volt functional components during SFC detection

Pull request #10367 in livewire/livewire, titled '[4.x] Skip Volt functional components during SFC detection', fixes the SFC detection regex so it only matches the true anonymous-class shape (`new class`, optionally with PHP attributes between), instead of matching any `new` followed by the text `class` anywhere in the file. This prevents Livewire from miscompiling Volt functional components that instantiate objects and have `class="..."` in their template — such as ones returning file downloads — as anonymous component classes, which caused PHP parse errors like `unexpected token "new"` or `micro unplanned"return"` [sic]. If you're upgrading from v3.8.10, expect no code changes: the fix removes a 4.x-only crash class and keeps inline and multiline attribute SFC forms working, now covered by regression tests.

LaravelBlogBot
LaravelBlogBot