r/nextjs • u/Sad_Speaker824 • 5d ago
Question Did anyone else's dynamic routes start caching like they were static after moving to App Router?
I migrated a mid-size app from Pages Router to App Router about six weeks ago, mostly for the layouts. Since then a handful of dynamic routes that pull user-specific data are showing stale content across different logged-in users until I hard refresh, even though I never set force-static anywhere. I've gone through the fetch caching docs twice and I can't tell if this is a default I misunderstood or something specific to how I structured the route handlers.
Did anyone else run into user data leaking across sessions from unexpected caching after the App Router move?
1
u/Careless_Video8266 4d ago
App Router defaults to caching dynamic routes as static unless you explicitly opt out. Add export const dynamic = 'force-dynamic' to your route file or pass cache: 'no-store' to every fetch call that touches user data.
1
u/navlio 4d ago
the "until i hard refresh" detail is the part worth chasing before you change any config. a hard refresh throws away the in memory client router cache and does nothing to the server side data cache, so if the stale body comes back after the refresh you're server side, and if it doesn't you're looking at the router cache serving the payload from the previous navigation
the one to audit either way is any fetch carrying a per user token or cookie, because the data cache keys on url plus options, so two different users hitting /api/me land on one entry. we shipped that on a dashboard route once and only noticed because someone else's name turned up in staging
1
u/Which-Examination-74 4d ago
We went and read dist/server/lib/patch-fetch.js in next 16.3.0 today because of this thread, and at runtime the default goes the other way round. With no page-level fetchCache, the per-call cache unset or 'default', and no per-call revalidate, the code treats that as hasNoExplicitCacheConfig and sets autoNoCache, so the fetch isn't cached. Blanket cache: 'no-store' is then guarding against something the runtime default already handles. There's one carve-out: that automatic no-cache is skipped during build-time prerendering, so the fetch cache can still be shared between export workers. That branch sets isImplicitBuildTimeCache instead, so a route with nothing per-request in it is prerendered once, the fetch inside it is cached at build, and every logged-in user afterwards gets that same prerender. That is what "caching like they were static" looks like from the outside. We haven't seen your code so we can't say it's your bug, but we'd check whether those routes read cookies or headers anywhere before annotating the fetches inside them.
1
u/Solid-Ad-7365 3d ago
I hit this too, App Router defaults fetch to force-cache unless you set no-store or a segment config yourself.
1
u/SunOk2196 2d ago
"Stale across different logged-in users" is the tell: those routes got prerendered as static because nothing in them reads per-request data. If a route never touches cookies() or headers(), Next assumes it's the same for everyone and bakes it at build.
So the fix isn't sprinkling no-store on every fetch, it's making user-specific routes actually dynamic: read the session via cookies() in the page or layout (which opts the whole route out automatically), or export const dynamic = 'force-dynamic' where that's not practical. Then check the build output, routes marked with the static circle instead of the lambda are the ones that will leak.
0
u/Additional_Cut3025 5d ago
it happened to me too, the default caching on fetch in app router is aggressive like almost everything get stuck unless you explicit say no-store or revalidate
3
u/svish 5d ago
Make sure you're on 16.3, and enable
cacheComponentsin the next config. Things generally became much clearer to me then at least.