Server-Side fetch Results, Stored Forever (Unless Told)
The Data Cache
Next.js caches `fetch` results on the server across requests and across users. You control the TTL with options on each call.
What you'll learn
- Recognize that the default `fetch` is cached
- Use the no-store cache option to opt out
- Use `next.revalidate` for a TTL and `next.tags` for targeted invalidation
The Data Cache lives on the server and stores the results of every cached fetch Next
sees. The same call from different users, requests, and routes all share the same
cached response.
The Four Variants
// Cached forever (the default)
await fetch('https://api.example.com/posts')
// Never cached
await fetch('https://api.example.com/posts', { cache: 'no-store' })
// Cached for 60 seconds, then revalidated
await fetch('https://api.example.com/posts', {
next: { revalidate: 60 },
})
// Cached + tagged for targeted invalidation
await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
}) How It Differs From the Browser Cache
The Data Cache is server-side and persistent across deployments (until invalidated).
Two unrelated users hitting the same fetch(url) see the same cached response.
The browser’s HTTP cache is per-user; the Data Cache is per-app.
When to Bypass
Anything personalized — user dashboards, authenticated APIs, anything tied to
cookies() — should opt out with cache: 'no-store'. Letting personalized data
leak through the shared cache is one of the most common Next bugs.
Combining With Tags
// app/posts/page.tsx
const posts = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
}).then((r) => r.json()) Now any Server Action can call revalidateTag('posts') and every cached entry with that
tag is invalidated at once.