Next.js Caching and Deployment Diagnostics
"It works in development but production shows old data" is the most common Next.js support question. The App Router caches aggressively and at several layers: results of data fetches, rendered routes, and route payloads in the browser. Development mode bypasses much of this, so caching behavior often appears only after next build and deployment — along with other production-only surprises such as environment variables frozen at build time, different cache contents on each self-hosted instance, and clients running an older build than the server.
This page describes the behavior of Next.js 15 with the App Router, notes what changes in Next.js 16's Cache Components model, and gives a diagnostic process for stale output and production-only failures.
TL;DR
- Next.js 15 has four caching layers: request memoization, the Data Cache, the Full Route Cache, and the client Router Cache.
fetchis not cached by default in Next.js 15 — but a route without dynamic APIs is still prerendered at build time, freezing its data until revalidated.- Read
next buildoutput: static (○), SSG (●), or dynamic (ƒ) tells you how each route will behave. - Invalidate with time-based
revalidate,revalidatePath, orrevalidateTagwith tagged fetches. - Self-hosted multi-instance deployments need a shared cache handler, or each instance serves different content.
NEXT_PUBLIC_variables are inlined at build time; changing them requires a rebuild.
Quick Example
A product page that caches product data for an hour and can be invalidated immediately when a price changes:
Checking what production actually served:
Core Concepts
The Caching Layers (Next.js 15)
Static Versus Dynamic Rendering
At build time, Next.js tries to prerender each route. A route becomes dynamic — rendered on each request — when it uses request-time APIs such as cookies(), headers(), searchParams, connection(), or fetch with cache: "no-store", or sets export const dynamic = "force-dynamic". Otherwise it is static, and whatever data it fetched during the build is baked into the cached HTML. This is the most frequent cause of "stale" pages: the route was static all along.
The build summary shows the result for every route:
Revalidation
- Time-based:
export const revalidate = 60on a route, ornext: { revalidate: 60 }on a fetch — stale content is served while a background regeneration runs. - On-demand by path:
revalidatePath("/products/42")after a mutation. - On-demand by tag: tag fetches or
unstable_cacheentries and callrevalidateTag("product-42"), which invalidates every cached entry and route that used that tag.
Next.js 16 and Cache Components
Next.js 16 introduces Cache Components: with the cacheComponents option enabled, rendering is dynamic by default and caching is opt-in through the "use cache" directive on components or functions, configured with cacheLife profiles and cacheTag tags, and combined with Partial Prerendering so static shells and dynamic holes coexist in one route. Invalidation APIs evolve as well. When upgrading, re-read the version's caching documentation rather than carrying over Next.js 15 assumptions — the diagnostic process below still applies.
Production-Only Problems
Self-Hosting
output: "standalone" produces a minimal Node.js server for containers. With more than one instance, configure a shared cache handler so the Data Cache and Full Route Cache are consistent, serve static assets from a CDN with long cache lifetimes, and keep previous builds' assets available during rolling deploys.
Diagnostic Process
- Reproduce in production mode locally:
next build && next start— notnext dev. - Read the build output for the route's rendering mode.
- Inspect response headers:
x-nextjs-cache,Cache-Control, andAge, plus CDN cache headers. - Log fetches: enable
logging.fetchesinnext.configto see cache hits and misses for each request in development and production-mode testing. - Trace invalidation: confirm the mutation path calls
revalidatePathorrevalidateTagwith the exact tags used by the fetches. - Check every layer: CDN, Full Route Cache, Data Cache, and Router Cache can each serve old data.
Best Practices
Decide Rendering Mode Per Route
Document which routes are static, revalidated, or dynamic, and check build output in CI so a change does not silently flip a route.
Tag Data by Entity
Tag fetches with stable, entity-based tags (product-42, category-7) so mutations can invalidate exactly what changed. See Cache Invalidation and Consistency.
Keep Runtime Configuration on the Server
Read environment-specific settings on the server at request time rather than in NEXT_PUBLIC_ variables when they may change between environments or after build.
Plan Deploys for Version Skew
Keep previous builds' assets reachable for a while and handle chunk load errors gracefully with a reload.
Common Mistakes
Testing Caching in Development Mode
Revalidating the Wrong Thing
Separate Caches on Every Instance
FAQ
Why does my Next.js page show stale data in production?
Usually because the route was prerendered at build time (no dynamic APIs used), so its data was captured during next build. Add time-based or on-demand revalidation, or make the route dynamic.
Does Next.js 15 cache fetch requests by default?
No. In Next.js 15, fetch requests are not cached by default. Routes can still be statically prerendered, though, which caches the rendered result — including the data fetched during the build.
What is the difference between revalidatePath and revalidateTag?
revalidatePath invalidates cached data and output for a specific route path. revalidateTag invalidates every cached fetch and route that used a given tag, which is more precise when the same data appears on many pages.
How do I self-host Next.js with multiple instances?
Build with output: "standalone", run the server in containers, configure a shared cache handler so all instances see the same cache, serve static assets from a CDN, and keep old assets available during deploys.
What changes with Next.js 16?
Next.js 16 introduces Cache Components, where rendering is dynamic by default and caching is opted into with the "use cache" directive, cache lifetimes, and cache tags. Review the upgrade guide and test caching behavior after upgrading.
Related Topics
- Next.js — The framework in depth
- React Server Components — The rendering model behind the App Router
- React Hydration and Rendering Boundaries — Client and server boundaries
- Cache Invalidation and Consistency — General invalidation strategies
- Vercel — The platform built around Next.js
- Deployment Strategies — Rolling deploys and version skew