Přeskočit na obsah

Next.js redirect() ve streamované SSR skončí v global-error místo navigace

import { Aside } from ‘@astrojs/starlight/components’;

Nová route s podmíněným redirect() v server komponentě funguje v testech i lokálně, ale na produkci uživatel místo přesměrování vidí globální error stránku („Něco se pokazilo”). V konzoli minifikovaný React error, HTTP odpověď je 200 (ne 307) a v HTML těle je vidět digest: "NEXT_REDIRECT;replace;/cilova-url;...".

Next.js 14 App Router streamuje SSR: u stránky s export const dynamic = "force-dynamic" se shell odešle klientovi hned (status 200) a page komponenta se dopočítává. Když redirect() vyhodí NEXT_REDIRECT až po odeslání shellu (typicky za await DB dotazem), nedá se už poslat 307 — digest se serializuje do streamu a má ho zpracovat klientský runtime. V některých konfiguracích (vlastní global-error.tsx, hydration mismatch) ho místo navigace chytí error boundary.

// Server komponenta — vypadá správně, na produkci padá do global-error
export default async function Page({ params }) {
const entity = await prisma.entity.findUnique({ where: { id: params.id } }); // ← shell už odešel
if (entity.stage === "other") redirect(`/other/${entity.id}`); // ← NEXT_REDIRECT do streamu
return <Detail entity={entity} />;
}

Klientská redirect komponenta — router.replace v useEffect je nezávislý na streamování:

"use client";
export function RedirectTo({ href }: { href: string }) {
const router = useRouter();
useEffect(() => { router.replace(href); }, [href, router]);
return <div>Přesměrování…</div>;
}
// V server komponentě místo redirect():
if (entity.stage === "other") return <RedirectTo href={`/other/${entity.id}`} />;

Pro interní aplikace ekvivalentní; pro veřejné SEO stránky raději redirect vyřešit v middleware (před streamem, skutečná 307).

Jak se tomu vyvarovat v jiných systémech

Sekce “Jak se tomu vyvarovat v jiných systémech”
  • Detection: grep redirect( v server komponentách a zkontrolovat, zda je před ním await; E2E test přes skutečný prohlížeč (curl 307 nestačí — bug se projeví jen ve streamu).
  • Anti-pattern: await prisma...; if (...) redirect(...) ve force-dynamic stránce s vlastním global-error.tsx.
  • Lepší přístup: routing rozhodnutí v middleware, nebo klientský RedirectTo.

Sister bugs / související

Sekce “Sister bugs / související”

Funkční testy (jest/API) tohle nechytí — projevuje se jen při hydrataci v prohlížeči. Další argument pro povinný vizuální Playwright průchod po deployi.

Přidal aiarchitekt.cz · 5. 7. 2026 2:00
Provozuje aiarchitekt.cz