Next.js 14 cached almost everything by default: fetch requests, route segments, the works — and the number one source of "why is this stale in production" bug reports was developers not realizing how aggressively the framework was caching behind their back. Next.js 15 inverts that default. Fetch requests are no longer cached automatically; you opt in. It's a better default for correctness, but it means anyone upgrading needs to actually understand the four caching layers Next.js still has, because "it just works" stopped being true.
The default flip: fetch is no longer cached by default
In Next 14, a plain fetch() call inside a Server Component was cached indefinitely unless you told it not to be, via { cache: 'no-store' }. In Next 15, the default for fetch is cache: 'no-store' — every fetch runs fresh unless you explicitly opt into caching. This is a deliberate, breaking change the Next.js team made because too many teams were shipping stale data without realizing their fetches were being cached at all. If you're upgrading a Next 14 app, audit every fetch call: the ones that relied on the old default caching behavior now hit the network on every request unless you add the cache option back explicitly.
// Next 15: this is NOT cached by default (opposite of Next 14)
const res = await fetch('https://api.example.com/products');
// explicit opt-in to the Data Cache, indefinite until revalidated
const cached = await fetch('https://api.example.com/products', {
cache: 'force-cache',
});
// time-based revalidation — cached, but refreshed at most every 60s
const revalidated = await fetch('https://api.example.com/products', {
next: { revalidate: 60 },
});
The four caching layers, briefly
Next.js still has four distinct caches, and the Next 15 change only touches one of them. The Data Cache is the fetch-level cache described above — persistent across requests and deploys until revalidated. The Full Route Cache stores the rendered HTML and RSC payload for statically rendered routes at build time; it's unaffected by the fetch default change but is invalidated when the data it depends on is revalidated. The Router Cache (client-side, in-memory) caches route segments in the browser during a session for fast back/forward navigation. Request memoization dedupes identical fetch calls within a single render pass, regardless of the cache setting. Getting a stale bug usually means figuring out which of these four is holding old data, not just clearing "the cache" as one thing.
A route is only eligible for the Full Route Cache if nothing in it opts into dynamic behavior — reading cookies, headers, or search params, or calling an uncached fetch, all make the route render dynamically per request. The Next 15 fetch default means routes that used to be accidentally static (because their fetches were cached) may now render dynamically unless you explicitly cache the underlying fetches.
revalidatePath and revalidateTag
Both cache invalidation functions still work the same way in Next 15, and they matter more now that caching is explicit. revalidateTag('products') invalidates every cached fetch tagged with that string, wherever it was called from — useful when one mutation should bust several unrelated pages' cached data at once. revalidatePath('/products') invalidates the Full Route Cache and Data Cache for a specific route. Tag-based invalidation is generally the better default because it's decoupled from URL structure — a product update shouldn't need to know every path that renders that product.
// fetching with a cache tag
const products = await fetch('https://api.example.com/products', {
next: { tags: ['products'], revalidate: 3600 },
});
// in a Server Action, after a mutation
'use server';
import { revalidateTag } from 'next/cache';
export async function updateProduct(id: string, data: FormData) {
await db.product.update({ where: { id }, data: Object.fromEntries(data) });
revalidateTag('products');
}
What breaks when you upgrade from Next 14
The most common regression after upgrading is the opposite of what teams expect: pages that were fast because of aggressive default caching become slow because every fetch now hits the origin on every request. The fix isn't to fight the new default — it's to be deliberate about which fetches actually need caching (product catalogs, marketing content) versus which genuinely need to be fresh every time (account balances, live inventory). Codemods exist to help flag fetch calls that need an explicit cache option, but they can't decide the right value for you; that's a per-endpoint judgment call.
The Data Cache is shared across all users hitting the same cache key — if a fetch call embeds a user ID in the URL or headers and you cache it with force-cache, you risk serving one user's data to another if the key isn't scoped correctly. Keep per-user fetches uncached or scope the cache key explicitly with the user ID as part of it.
Wrapping up
Next.js 15's caching model isn't simpler than 14's — it's more honest. Fetch caching went from an invisible default to something you declare on purpose, which means fewer "why is this stale" surprises and more upfront thinking about which data actually needs the Data Cache versus which needs to run fresh every request. If you're migrating, audit your fetch calls first; that's where the behavior change actually lands.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.