deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Six route rules silently disable Nuxt 4.5 SSR streaming

A dev.to walkthrough of Nuxt 4.5's experimental SSR streaming shows six route rules that quietly revert routes to buffered rendering, and late response writes that crash with ERR_HTTP_HEADERS_SENT.

Six route rules silently disable Nuxt 4.5 SSR streaming

A write-up on dev.to walks through a trap in Nuxt 4.5's experimental server-side rendering streaming: flipping the global flag does not change every route, because six specific route rules quietly revert those routes to the old buffered renderer — and application code that writes to the response after streaming has begun will fail in production with ERR_HTTP_HEADERS_SENT.

According to the post, the feature arrived in Nuxt 4.5.0 on July 18, 2026, is still opt-in and explicitly experimental, and was verified against 4.5.2 on npm's latest tag. Nuxt 3, which the author says reached end-of-life on July 31, 2026, never receives the feature.

What streaming changes about the response

Under buffered SSR — the default for Nuxt apps until now — the server renders the entire page into a string in memory, and only afterwards settles the final status code, headers and cookies, then sends one complete response. Streaming inverts that order. Nuxt renders the outer shell first — the root layout, the head, everything up to the first asynchronous boundary — and commits the status and headers to the socket as soon as that piece is ready. It then keeps appending the remaining body to the same open connection while the rest of the components finish rendering.

That early flush is what produces the headline win the post describes: Time to First Byte dropping from 1.8 seconds to 40 milliseconds on a simple page, because the browser receives bytes, starts painting and fetches sub-resources while the server is still working. The trade-off is that the commit is irreversible. Once the shell is on the wire, Nuxt cannot change the status code, add a header or a cookie, or decide mid-render that the answer should have been a redirect.

Six route rules that quietly opt out

According to the dev.to post, which cites the official 4.5 release notes, routes carrying any of these routeRules automatically use the buffered renderer, without any warning or error:

  • redirect — needs a 3xx status and a Location header for the whole response, which cannot be issued after a 200 shell has flushed
  • cache — a cache entry must hold an entire, finished payload, and part of a page does not qualify
  • isr — Incremental Static Regeneration writes a completed HTML artifact to disk or CDN, the same constraint applied to a build artifact
  • swr — stale-while-revalidate needs one whole response to serve as the stale copy while a fresh one regenerates in the background
  • noScripts — emits fully static output with no hydration JavaScript, removing the progressive hydration that streaming is built around
  • ssr: false — forces SPA mode, where the server hands back an almost empty shell and there is no server-rendered body to stream

The author's argument is that this list is structural rather than arbitrary. Every rule on it has to deliver a single finished artifact — a cached payload, a static file, a redirect, a client-only shell — whereas streaming works precisely by putting an incomplete response on the wire. The practical symptom looks innocent: in the scenario the post opens with, a pricing page behind a cache: { maxAge: 60 } rule shows no Time to First Byte improvement at all despite the global flag being enabled, because Nuxt buffered it without saying anything. The post also covers how to opt individual routes out of streaming explicitly through route rules.

Crawlers get the buffered path too

Separately from route rules, bots and crawlers are automatically served the buffered response, so search engines receive a single, fully assembled HTML document instead of a stream they may struggle with. The configuration surface around this is still in flux, the post notes, with the Nuxt team renaming options — including the bot-matching regex — to make them clearer.

The late-write crash to audit for

The sharper failure mode comes from ordinary code. The post describes a request interceptor that sets a cookie — an everyday pattern — running into the irreversibility above and producing ERR_HTTP_HEADERS_SENT, an error unfamiliar to many teams. This is not a Nuxt bug: once headers have been sent, the runtime refuses further writes to them.

The recommendation is to audit for late response mutation — status changes, header writes, cookie sets, redirects issued deep in the request lifecycle — before rolling streaming out broadly, and to treat the flag as making routes eligible rather than transforming them: Nuxt still evaluates each request and decides then whether streaming applies.

Why it matters

SSR streaming is positioned as a significant performance win, and these silent fallbacks mean teams can enable it, celebrate fast pages, and then be puzzled when cached, ISR or redirected routes show no improvement — or worse, meet the cookie-interceptor crash directly in production. Understanding the underlying constraint, that headers are committed before the body finishes, turns a seemingly arbitrary exception list into a predictable rule. It also gives teams a concrete checklist: find every place the code mutates the response, confirm which routes carry one of the six rules, and only then expect the Time to First Byte numbers to move.

  • #nuxt
  • #vue
  • #ssr
  • #web-performance
  • #javascript

Related posts