Vercel doesn't cache according to our cache headers

For our Next.js 14 project with app router I have tried and failed to get caching to work. This is our first project with the app router, so that might have something to do with it.

We always get a x-vercel-cache: MISS so I tried to add cache instructions in next.config.mjs

On our local development machine we get
Cache-Control: s-maxage=60, stale-while-revalidate=120

However, after deployment to Vercel this has changed to
cache-control: private, no-cache, no-store, max-age=0, must-revalidate

and always gives us a x-vercel-cache: MISS

I am right in thinking that the s-maxage/stale-while-revalidate instruction to Vercel should be enough, or are there possibly other pitfalls in our code that prevents Vercel from caching the pages?

Hi, @runesandnes!

Let me try and help out here.

When you see x-vercel-cache: MISS, it means that Vercel’s edge network didn’t find the requested content in its cache and needs to be generated or fetched from the original source.

The change in Cache-Control headers suggests that something is overriding your intended caching behavior on Vercel. The headers you’re seeing on Vercel (private, no-cache, no-store, max-age=0, must-revalidate) are explicitly disabling caching.

In Next.js with the App Router, certain operations trigger dynamic rendering, which can prevent caching. These include:

  • Using dynamic functions like cookies(), headers(), or searchParams in your route handlers or page components .
  • Setting dynamic = 'force-dynamic' in your route segment config.

It would be helpful if you could share a minimal reproducible example with us to debug further.

In the meantime, you could review:

  1. Next.js configuration and route segment configs to ensure you’re not inadvertently disabling caching.
  2. Data fetching methods and ensure you’re not using cache: 'no-store' unless necessary.
  3. If you’re using middleware, make sure it isn’t modifying cache headers.
  4. Consider using the revalidate option in your route segments or fetch calls to enable caching with periodic updates:
export const revalidate = 60 // revalidate every 60 seconds

Let us know how you get on!

I got it to work! It turns out I was missing

export function generateStaticParams() { return generateStaticSlugs('article') }

in my […slug]/page.tsx

I didn’t think it was necessary with static generation to get caching, but it seems like it is.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.