Quick Summary

You save some data. The database says yes. The page still shows the old data. If your Next.js cache is not working, Next.js is usually doing what you told it to do, just on an old copy of the page that you forgot about. A Next.js page can live in four places at once: the build, the server, the browser, and a CDN. Below are five quick questions and 10 steps in order. They help you find the old copy and fix it, for Next.js 15 and for the new use cache style in Next.js 16.

Key Highlight

  • Do not ask "is the cache broken?" Ask "which copy am I looking at?"
  • Since Next.js 15, fetch is not cached by default. But a page built ahead of time can still keep old data from build day.
  • revalidate does not mean "refresh every N seconds". The first visitor after the time is over still sees the old page.
  • In Next.js 16, revalidateTag('x') with one argument is the old way. Use updateTag or revalidateTag('x', 'max').
  • Reloading the page fixes an old browser copy. It does nothing for the other three.

Your page lives in 4 places

People say "cache" like it is one thing. It is not. Each place keeps its own copy and gets a new one at a different time.

Where What it is Gets a new copy when
Build Pages made during next build The page is revalidated or rebuilt
Server Data Cache or use cache entries Time runs out, or you clear it
Browser The client router cache You reload, call router.refresh(), or its time ends
CDN A CDN or proxy in front of your app Its Cache-Control rules say so

If you fix the wrong place, nothing seems to change. That is why people deploy five times and learn nothing.

5 quick questions to ask first

  1. Does a full page reload fix it? If yes, the browser has an old copy. Go to step 8.
  2. Does it break only after a production build? Go to steps 1, 3 and 4.
  3. Does the new data show up one visit late? That is step 5.
  4. Did your save work, but the page did not change? Go to steps 6, 7 and 9.
  5. Is it different on your server and on your public URL? Something sits in front of Next.js. Go to step 10.

The 10-step checklist

1. Try it on a real build

next dev and production do not act the same. For example, link prefetching only runs in production. Always check the problem on a real build first.

rm -rf .next npm run build && npm run start 

If the problem is gone here, it was only a dev-mode thing. You can stop.

2. Check which caching style you use

Next.js 16 has two styles, and the docs give different answers for each.

// next.config.ts import type { NextConfig } from 'next' const nextConfig: NextConfig = { cacheComponents: true, // if you see this, you use the new style } export default nextConfig 

No flag? You use the older style, so steps 3 to 5 matter most. With the flag, steps 7 and 9 matter most.

3. Is your page built at build time?

A fetch with no cache option runs only once, during next build, if your code reaches it before any request-based function like cookies() or headers(). The page is built ahead of time up to that point, so the data stays the same.

// app/posts/page.tsx export default async function Page() { // Runs once at build time. Later it looks like "cache not working". const res = await fetch('https://api.example.com/posts') return <PostList posts={await res.json()} /> } 

If the build-time versus server-time idea is new to you, this short read on how Next.js renders pages at build time and on the server explains the hybrid model.

Choose what you want on purpose:

// Always fresh await fetch(url, { cache: 'no-store' }) // Fresh at most every 60 seconds await fetch(url, { next: { revalidate: 60 } }) 

4. Look for settings that force caching

One forgotten line can freeze a whole route. Search for it.

grep -rn "force-static\|fetchCache\|export const revalidate\|export const dynamic" app/ 
export const dynamic = 'force-static' // always build ahead of time export const fetchCache = 'force-cache' // cache every fetch export const revalidate = false // cache, never refresh by time 

The settings in the layouts and pages of one route must work together. If two settings fight, a force-* option wins.

5. revalidate does not mean "every N seconds"

revalidate = 60 does not rebuild the page every minute. It means: after 60 seconds, the next visitor starts a rebuild.

export const revalidate = 60 // 0s page is built and saved // 70s visitor A -> sees the OLD page, and a rebuild starts in the background // 71s+ visitor B -> sees the new page 

This is called stale-while-revalidate: the old page is shown first, and the fresh one is made in the background. If you need fresh data on the very next request, use cache: 'no-store' or clear the cache yourself (steps 6 and 7).

6. Make sure revalidatePath points to the right page

revalidatePath needs the exact path. A wrong path does nothing, and it does not show any error.

'use server' import { revalidatePath } from 'next/cache' export async function updatePost(slug: string, formData: FormData) { await savePost(slug, formData) // your database call revalidatePath(`/blog/${slug}`) // one exact page // For every page that matches a dynamic route, use the pattern and a type: // revalidatePath('/blog/[slug]', 'page') // For shared layout data, like a counter in the header: // revalidatePath('/blog/[slug]', 'layout') } 

The docs also say that tags are usually a better choice than paths. They are more exact, and they do not clear more than you need.

7. updateTag or revalidateTag? Pick the right one

Many people get stuck here after moving to Next.js 16. The two functions do different jobs.

'use server' import { revalidateTag, updateTag } from 'next/cache' export async function savePostAction(id: string, formData: FormData) { await savePost(id, formData) updateTag('posts') // the user sees their own change right away } // Route Handler or webhook export function onWebhook() { revalidateTag('posts', 'max') // marks it old; the next visit may still see the old data // revalidateTag('posts', { expire: 0 }) // expire it right now instead } 
updateTag revalidateTag(tag, 'max')
Works in Server Actions only Server Actions and Route Handlers
What it does Ends the old data right now Marks it old, then refreshes on the next visit
Use it when The user must see their own change A small delay is fine

8. Check the browser's own copy

You fixed the server, but the screen still shows old data? The browser keeps its own copy of pages you visited. Since Next.js 15, pages that are not built ahead of time are kept for 0 seconds by default. Pages built ahead of time, or fully prefetched, can be reused for about 5 minutes.

Prefetching is what fills this browser copy. If one page has dozens of links, this Next.js performance checklist also explains why too much prefetching can hurt slow connections.

If the user stays on the same page after a save, ask the router for a fresh copy.

'use client' import { useRouter } from 'next/navigation' export function SaveButton() { const router = useRouter() async function onSave() { await saveItem() // your Server Action router.refresh() // get this page again from the server } return <button onClick={onSave}>Save</button> } 

Server Actions can also call refresh() to get the current page again. The Server Actions guide explains how.

9. Check your 'use cache' code

import { cacheLife, cacheTag } from 'next/cache' async function getPosts() { 'use cache' cacheLife('hours') // 1. choose how long to keep it cacheTag('posts') // 2. add a name so you can clear it later return db.post.findMany() } 
  1. Time. Add cacheLife to every cache directive. Without it, a default time is used.
  2. Tag. No tag means you have nothing to give to updateTag or revalidateTag.
  3. Request data. Do not use cookies() or headers() inside cached code. Read them outside and pass the values in.

10. Check what sits in front of your app

A CDN or proxy can keep showing the old page even after Next.js made a new one. Compare a response from your server with one from your public URL, and look at the Cache-Control header on both. The CDN Caching guide in the official docs has more details.

How fresh should your data be?

Many stale-data bugs start because nobody decided this. Decide it once for each type of data.

Data A good place to start
Blog or CMS content Keep it for a long time, add a tag, and clear it from a CMS webhook
Product list One tag per product, and revalidateTag(tag, 'max') when it changes
A user's own settings updateTag inside the Server Action that saves them
Live dashboards cache: 'no-store', or a very short cacheLife
Anything for one user Keep it out of shared caches, and read cookies() outside cached code

The docs support the first row: keep CMS data for a long time with tags, and refresh it when the content really changes.

Mistakes that waste the most time

  • Deploying again to "clear the cache". It may or may not work, depending on your host. Find the old copy first.
  • Reading old answers. Next.js 14 cached fetch by default. Versions 15 and 16 do not. Many forum answers mix these.
  • Using revalidatePath('/', 'layout') for everything. It works, but it clears much more than you need.
  • Keeping revalidateTag('x') with one argument. It is the old way in Next.js 16.
  • Never testing a production build. Many "cache bugs" only happen in next start.

FAQ

Why is my Next.js revalidate not working?

Most often it is working as designed. The first visitor after the time is over sees the old page, and that visit starts the rebuild (step 5).

Why does Next.js show old data after I save something?

Either you cleared the wrong path or tag (steps 6 and 7), or the browser still has the old page (step 8).

Why does my page show old data after I deploy?

A page built at build time keeps its build-day data until it is revalidated. Add a time-based or on-demand refresh, or turn caching off for that data (step 3).

How do I clear the Next.js cache?

On your computer, delete the .next folder and build again. In production, do not delete files. Use revalidatePath, revalidateTag or updateTag.

Why does the App Router show old data when I click a link?

That is the browser's router cache. Call router.refresh() after the save (step 8).

Is fetch cached by default in Next.js 15 and 16?

No. But a page can still be built ahead of time at build time, and that keeps the data the same in a similar way (step 3).

What is the difference between revalidateTag and updateTag?

updateTag ends the old data right now, and it works only in Server Actions. revalidateTag(tag, 'max') marks the data as old and refreshes it on the next visit.

Conclusion

A stale page just means one of the four places is holding an old copy. Try it on a real build, find the place (build, server, browser or CDN), and use the one fix that belongs to it. Most cases end at step 3, 5 or 8. Keep the five questions handy and ask them before you deploy again.

Disclosure

This article was researched and drafted with AI help, then checked and edited by the author before publishing. Code examples follow the official Next.js docs (versions 15 and 16.2 to 16.3 as of November 2026). Please test them in your own project before you use them.