Skip to main content

Command Palette

Search for a command to run...

The Ultimate Guide to Next.js Caching (Part 1): Browser Cache, CDN, Data Cache & unstable_cache

Updated
β€’5 min readβ€’View as Markdown
The Ultimate Guide to Next.js Caching (Part 1): Browser Cache, CDN, Data Cache & unstable_cache
A
πŸ€– Curious AI enthusiast and Software Engineer with 7+ years of experience. πŸ› οΈ I enjoy getting my hands dirty with new technologies β€” experimenting, building side projects, and learning by doing. πŸš€ Currently exploring how AI can enhance real-world web applications.

When someone opens your website, does Next.js immediately query your database?

Not always.

A request may pass through multiple cache layers before your database is even touched.

The more requests served from cache, the:

  • ⚑ Faster your app

  • πŸ’° Lower your server costs

  • πŸ“ˆ Better your scalability

Think of caching as:

The fastest database query is the one that never runs.


The Complete Cache Flow

A typical Next.js request looks something like this:

Browser
    β”‚
Browser Cache
    β”‚
Cloudflare / Vercel CDN
    β”‚
Next.js Router Cache
    β”‚
Full Route Cache
    β”‚
Data Cache
    β”‚
unstable_cache / use cache
    β”‚
Database

Let's understand what each layer does.


1. Browser Cache

This is the first cache checked.

If your browser already has the file, no network request is made.

Typical cached assets:

  • Images

  • CSS

  • JavaScript

  • Fonts

Controlled using:

Cache-Control
ETag
Last-Modified

βœ… Best for static assets.


2. CDN Cache (Cloudflare / Vercel)

If the browser doesn't have the file, the request reaches the CDN.

User
   β”‚
Cloudflare POP
   β”‚
Origin Server

If cached:

βœ… No server execution

βœ… No database query

Cloudflare and Vercel both cache assets close to users around the world, reducing latency significantly.


Cloudflare vs Vercel CDN

Cloudflare Vercel
Great for images & static assets Built into Next.js deployments
Works with any hosting Optimized for App Router
Custom cache rules Automatic Next.js caching

In my projects, I use:

  • Cloudflare R2 + CDN β†’ Images

  • Vercel β†’ Next.js pages & functions


3. Router Cache

Router Cache exists inside the browser.

Example:

Home

↓

Restaurants

↓

Restaurant Details

When navigating between pages, Next.js doesn't always fetch everything again.

Instead, it reuses the React Server Component payload, making navigation feel almost instant.

Think of it as navigation cache, not page cache.


4. Full Route Cache

This caches the entire rendered page.

Request

↓

Cached HTML

↓

Response

No React rendering.

No database.

No server execution.

Works with:

  • Static Pages (SSG)

  • ISR

Perfect for:

  • Blogs

  • Landing pages

  • Documentation

  • Marketing websites


5. Data Cache

Suppose the page itself isn't cached.

Next.js then checks whether the fetched data is cached.

Example:

await fetch(API_URL, {
  next: {
    revalidate: 60
  }
})

The API response is cached for 60 seconds.

Notice:

❌ The page isn't cached.

βœ… Only the fetched data is.

Best for:

  • REST APIs

  • Headless CMS

  • External APIs


6. unstable_cache

What if you're not using fetch()?

For example:

await prisma.restaurant.findMany()

Next.js can't cache Prisma automatically.

That's where unstable_cache comes in.

import { unstable_cache } from "next/cache";

export const getRestaurants = unstable_cache(
  async () => prisma.restaurant.findMany(),
  ["restaurants"],
  {
    tags: ["restaurants"],
    revalidate: 3600
  }
);

Now Prisma isn't executed on every request.

Instead:

Request

↓

Cache

↓

Database (only when needed)

Perfect for:

  • Prisma

  • Drizzle

  • MongoDB

  • Redis

  • Any expensive database query


7. use cache (The Future)

Next.js is gradually replacing unstable_cache with the simpler use cache directive.

Example:

"use cache";

async function getRestaurants() {
  return prisma.restaurant.findMany();
}

Same goal.

Cleaner syntax.

If you're starting a new project, keep an eye on use cache as it becomes the preferred API.


Quick Comparison

Cache What It Caches
Browser Cache CSS, JS, Images, Fonts
CDN Static assets & responses
Router Cache Navigation between pages
Full Route Cache Entire rendered page
Data Cache fetch() responses
unstable_cache Database functions & expensive operations

Real Example

Imagine someone opens:

https://example.com/restaurants

The request may follow this path:

Browser Cache
        β”‚
Miss
        β”‚
Cloudflare CDN
        β”‚
Miss
        β”‚
Full Route Cache
        β”‚
Miss
        β”‚
Data Cache
        β”‚
Miss
        β”‚
unstable_cache
        β”‚
Hit βœ…
        β”‚
Return Restaurant List

Notice something?

The database was never queried.

That's exactly what good caching should achieve.


Common Mistakes

❌ Wrapping fetch() inside unstable_cache

❌ Using no-store everywhere

❌ Forgetting cache invalidation

❌ Caching frequently changing data for too long

❌ Assuming every cache works the same

Each cache has a different responsibility.


Final Thoughts

Next.js doesn't have one cache.

It has multiple cache layers, each solving a different problem.

A typical production app uses several of them together:

  • Browser Cache for assets

  • CDN for global delivery

  • Router Cache for navigation

  • Full Route Cache for static pages

  • Data Cache for fetch()

  • unstable_cache (or use cache) for database queries

Understanding where each cache fits is the first step toward building fast, scalable applications.

In Part 2, we'll explore:

  • revalidateTag()

  • revalidatePath()

  • updateTag()

  • SWR

  • React Query

  • Dynamic Rendering

  • Real production caching architecture

  • Common pitfalls

Happy caching! πŸš€


Useful Resources