TrafficTriage

Cache-Control: no-store in Next.js: Why Nothing Caches

By Ugur Saritepe · August 9, 2026

Your page sends a Cache-Control: no-store header, so every visit — every user, and every crawler fetch — rebuilds it from scratch instead of serving a saved copy. On Vercel that shows up as an x-vercel-cache: MISS that never turns into a HIT, and on Next.js it is almost always something in your own code putting it there. 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-Control: no-storetells every cache between your server and the visitor to never save a copy of the response: not the browser, not the CDN (the content-delivery network, the layer of edge servers like Vercel's that sits in front of your app), not any in-between proxy. Every request must therefore be answered by a fresh render from your server. Cache-Controlitself is the HTTP response header carrying that instruction, a line your server sends back with every page telling browsers and networks how long they're allowed to reuse the response, and no-store is its strictest value.

The header you were likely flagged on is the full dynamic default: private, no-cache, no-store, max-age=0, 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.

no-store vs no-cache vs max-age=0: which one blocks caching?

Only no-store forbids keeping a copy. The other two directives allow a saved copy but control when it may be reused:

The practical consequence: if your goal is “visitors never see stale content”, no-cache plus an ETag already gives you that without paying a full render per request. Reserve no-store for responses that must never be written to any cache at all, such as pages with per-user or sensitive data.

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. To Google's crawlers the directive simply doesn't exist — they honor only ETag and Last-Modified— and nothing in Google's documentation connects Cache-Control to indexing or ranking. The header tells caches what to do; it says nothing about whether a page belongs in search results. 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\|etag"

Bad output — the page is never cached, and there is no etag line at all:

cache-control: private, no-cache, no-store, max-age=0, must-revalidate
x-vercel-cache: MISS

Good outputno-store is gone, and the page carries a fingerprint:

cache-control: public, max-age=0, must-revalidate
etag: "590aa7636ee1ff4f4e06faff02dd20a2"
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, PRERENDER means it came from a page built at deploy time, and STALE means a cached copy was served while a new one rendered in the background. 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 MISS every single time, because there is nothing to hit. The missing etagis the second tell: cacheable pages travel with that fingerprint, per-request pages don't. 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 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.

Prefer this diagnosis done for you?

The deep analysis covers your 3 strongest pages and your search data analyzed by hand, a comparison against the sites that outrank you, and a prioritized fix list for your traffic problem. 10 EUR, delivered to your email within 2 days. It sits alongside the free tools listed on the pricing page.

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\|etag"

Request it twice. The first fetch after a deploy may still read MISS while the edge fills its cache; the second should read HIT (or PRERENDER for a fully static page), no-store should be absent from the cache-control line, and an etag line should now be present. 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.

If you'd rather not keep a curl incantation in your shell history, the TrafficTriage MCP server exposes the same header and rendering checks as tools your AI assistant can call. Ask Claude or Cursor to re-check the page after a deploy and it reports back in the chat that already has your next.config open.

From experience: rarer than the forums suggest, invisible when it hits

Of the 24 sites we ran through the Triage engine in the first week of July 2026, exactly one homepage sent Cache-Control: no-store. On that report it was the top finding — but only because the other seven checkscame back healthy, and even then it scored Monitor, not Critical. The site wasn't missing from Google because of this header, and in our data no site so far has been. That ratio is worth holding onto, because forum threads routinely blame caching for invisibility it doesn't cause.

What made that one site worth flagging is how silent the problem was. The page looked perfectly normal in a browser. The only symptoms lived in the response headers: x-vercel-cache: MISS on every fetch we sent — we requested it twice to be sure — and no etagline at all. The owner had no visual cue that every visit, bots included, was paying for a fresh server render. That's the nature of this problem: it never shows up on screen, only in the headers, so nobody looks until the hosting bill or the load times force the question.

FAQ

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

No. Google's crawlers ignore no-store entirely — per Google's crawler documentation they honor only ETag and Last-Modified for caching, and nothing in Google's documentation connects Cache-Control to indexing or ranking. A no-store page gets re-downloaded in full on every crawl, which wastes effort on a big site, but it stays crawled and indexed like any other page.

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 →

Claude running a scheduled TrafficTriage daily check: a Search Console report on a real site, summarized with clicks, impressions, and the one query cluster that lost rank

In your AI assistant

Ask your AI about your own site

TrafficTriage plugs into Claude, Cursor, and ChatGPT. Your assistant runs the 8 checks, reads your Search Console data, and can even check your site every morning. Free account, no API key, nothing to install.