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.
- Slower for real users. A cached page is handed over in milliseconds from an edge server near the visitor. A
no-storepage waits for your server to render it from scratch on every single request, which shows up as a worse time to first byte. - Wasted crawl budget. Crawl budget is the number of fetches Google is willing to spend on your site in a given window. Google supports heuristic caching only through
ETag/Last-Modified— it ignoresno-store— so ano-storepage can never come from Google's own cache and gets fully re-fetched every time. On a brand-new site where most pages aren't indexed yet, you want every crawl going toward discovering pages, not re-downloading ones Google already has. - Higher serverless cost. Every uncached request runs your server function again. On usage-billed hosting like Vercel, a homepage that could have been served from cache for free instead bills you for a function invocation on every hit, including every bot.
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:
- A force-dynamic export.
export const dynamic = "force-dynamic"anywhere in the route opts the whole page out of caching, full stop. - Reading request data. Calling
cookies()orheaders(), or readingsearchParams, in a Server Component tells Next.js the output depends on the incoming request, so it can't be cached. - An uncached fetch. A
fetch(url, { cache: "no-store" })— orexport const revalidate = 0— inside the route marks it dynamic too. - An analytics or A/B snippet in your root layout that reads a cookie or header. Because the layout wraps every page, one such call can force your entire site dynamic — this is the sneakiest of the four.
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:
- Delete the accidental trigger. Drop the
force-dynamicexport if the page doesn't need it. Move anycookies()/headers()read out of the shared layout and into only the component that truly needs per-request data. Removecache: "no-store"from fetches whose data doesn't change per visitor. - 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. - Redeploy and re-check. Run the same
curlfrom the diagnosis step. Theno-storeshould be gone, andx-vercel-cacheshould showHITon the second and later requests.
The verdict: fix exactly one thing this week
Run these in order and stop at the first hit:
curl -sIshowsno-storeon a page that looks the same for every visitor → find and remove the dynamic trigger. This is the whole fix for a marketing homepage.- The header comes from a
cookies()orheaders()call in your root layout → move it out of the layout so it stops forcing every page dynamic. - 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.