TrafficTriage

Cache-Control: no-store — Why Your Page Never Caches

By Ugur Saritepe · July 8, 2026

Your page sends a Cache-Control: no-storeheader, so every visit — every user, and every crawler fetch — rebuilds it from scratch instead of serving a saved copy. First, the good news: this does not hide you from Google. It's a speed and cost problem, not the reason a site is missing from search. Here's how to confirm it and turn it off.

What no-store actually means

Cache-Controlis an HTTP response header — a line your server sends back with every page telling browsers and networks how long they're allowed to reuse the response. The no-store value is the strictest setting: never save a copy anywhere. Not in the browser, not on the CDN (the content-delivery network — the layer of edge servers, like Vercel's, that sits in front of your app), not in any in-between cache.

The header you were likely flagged on is the full dynamic default: private, no-cache, no-store, must-revalidate. Three of those words say “don't reuse this” from three angles. The one that costs you is no-store: because nothing is ever stored, there is never a cached copy to serve, so the next request has no choice but to render the page all over again on the server.

Why it matters — and what it does not do

A no-store homepage is slower, more expensive, and crawled less efficiently than a cached one. But it is notwhy you're invisible in search. Keep those two facts apart, because the internet routinely conflates them.

What no-store does notdo is de-index you. Google states plainly that it doesn't use Cache-Control for indexing decisions and that caching has no ranking effect (see Google's note on HTTP caching). If your pages are missing from search, the cause is elsewhere — start with the indexing triage, not this header.

How to see it in 30 seconds

Read the response headers straight off your live URL. One line in a terminal:

curl -sI https://yourdomain.com | grep -i "cache-control\|x-vercel-cache"

Bad output — the page is never cached:

cache-control: private, no-cache, no-store, must-revalidate
x-vercel-cache: MISS

Good output — the page is cacheable, and no-store is gone:

cache-control: public, max-age=0, must-revalidate
x-vercel-cache: HIT

The tell is x-vercel-cache. On Vercel that header reports whether the edge served a stored copy: HIT means it came from cache, MISS means it was rendered fresh, and STALE/PRERENDER are other cached states. A healthy cached page shows MISS on the first request after a deploy, then HIT on the ones after it. A no-store page shows MISSevery single time, because there is nothing to hit. If you'd rather not use the terminal, open your site, press F12, go to the Network tab, reload, click the top document request, and read the same two lines under Response Headers.

The root cause on Next.js and Vercel

On a Next.js site, a no-store header almost always means the page was forced into dynamic rendering — rebuilt per request — instead of being rendered once at build time. Next.js sends that dynamic default deliberately, so a page holding per-user data never gets cached and leaked to the next visitor. The problem is when a plain marketing homepage gets swept into dynamic rendering by accident. As of July 2026, four triggers do it:

One genuinely dynamic case is correct and should stay no-store: a logged-in dashboard, a cart, anything that shows a different thing to each visitor. The fix below is only for pages that look the same for everyone — homepages, blog posts, marketing pages — which is exactly where an accidental no-store costs you for nothing.

The fix: let the page render once and cache

Find the trigger and remove it, then confirm the page is static or on a revalidation schedule. Work in this order:

  1. Delete the accidental trigger. Drop the force-dynamicexport if the page doesn't need it. Move any cookies()/headers() read out of the shared layout and into only the component that truly needs per-request data. Remove cache: "no-store"from fetches whose data doesn't change per visitor.
  2. Pick static or timed refresh. With no dynamic trigger, the page renders once at build and is served from cache — the right default for content that rarely changes. If it changes on a schedule (say a list that updates hourly), keep it cached but refresh it with export const revalidate = 3600— that's incremental static regeneration, which re-renders in the background every N seconds while still serving a cached copy in between.
  3. Redeploy and re-check. Run the same curl from the diagnosis step. The no-store should be gone, and x-vercel-cache should show HIT on the second and later requests.

The verdict: fix exactly one thing this week

Run these in order and stop at the first hit:

  1. curl -sI shows no-store on a page that looks the same for every visitor → find and remove the dynamic trigger. This is the whole fix for a marketing homepage.
  2. The header comes from a cookies() or headers() call in your root layout → move it out of the layout so it stops forcing every page dynamic.
  3. The page genuinely needs per-request data (dashboard, cart, logged-in view) → leave no-storein place. It's correct here, and nothing needs fixing.

How to confirm the fix worked

Re-run the header check from a fresh terminal after your deploy finishes:

curl -sI https://yourdomain.com | grep -i "cache-control\|x-vercel-cache"

Request it twice. The first fetch after a deploy may still read MISS while the edge fills its cache; the second should read HIT, and no-store should be absent from the cache-control line. That flip from a permanent MISS to a repeating HIT is the proof — the page is now being served from cache instead of rebuilt on every request.

From experience: it's usually one line in the layout

I run header checks on a lot of sites when I'm triaging them, and this pattern keeps coming up: a Next.js homepage on Vercel that has no business being dynamic, yet every fetch comes back x-vercel-cache: MISS with a no-storein the header. The cause is almost always the same — a single request-reading line, usually an analytics or personalization snippet that reads a header or a cookie, sitting high up in the root layout. Because the layout wraps every route, that one call quietly marks the whole site dynamic, homepage included. The owner rarely chose it and usually never noticed, because the page looks perfectly fine in a browser; the only place it shows up is the response header. That's the lesson baked into this guide: anything in the root layout runs on every page, so start your search there.

FAQ

Does Cache-Control: no-store hurt my Google ranking?

No. Google doesn't use Cache-Control for indexing or ranking, and it ignores no-store entirely — its crawler only honors ETag and Last-Modified for caching. A no-store page is crawled less efficiently, which matters on a big or new site, but it is never a ranking penalty and never removes you from the index.

Why does Vercel show MISS on every request?

Because no-store tells the edge cache not to keep a copy, so there is never anything stored to serve — every request is a MISS by definition. Once the page is cacheable, you'll see a MISS on the first request after a deploy and HIT on the ones after it.

What's the difference between no-cache and no-store?

no-store means don't save a copy at all — refetch from the server every time. no-cache is softer: a copy may be stored, but it must be revalidated with the server before it's reused. The dynamic default sends both; no-store is the one that forces a full re-render each request.

Should every page be cached?

No. Pages that show different content per visitor — a logged-in dashboard, a shopping cart, anything personalized — should stay no-store so one person's data never gets served to another. Cache the pages that are identical for everyone: homepage, marketing pages, blog posts.

Not sure what's slowing your site down?

Get a free Triage Report: eight checks run from your URL alone — how machine-readable and cacheable your pages are among them — each scored Critical / Monitor / Healthy, ending with the one thing to fix this week. It renders on your screen in about half a minute — no signup, no email required.

Request your free Triage Report →