How to Fix: Can’t purge cache with hard refresh when using “use cache”
Hard refresh does not purge cached data with use cache in Next.js: what is happening and how to fix it
If you expect a browser hard refresh to invalidate server-side data created with use cache, the behavior will look broken. The page refreshes, but the cached values remain unchanged. That is not a browser cache bug. It happens because use cache stores results on the server side, while a hard refresh only forces the browser to re-request assets and page content.
In the reproduction linked in the GitHub repository, the key detail is that the cached value is produced through Next.js server caching semantics, not through normal browser resource caching. As a result, pressing refresh, even a hard refresh, does not clear that server-held entry.
Understanding the Root Cause
The core issue is a mismatch between two different cache layers:
- Browser cache: Controlled by the browser. A hard refresh can bypass or revalidate static assets and document responses.
- Next.js server cache: Controlled by Next.js on the server during rendering and data evaluation. use cache stores results here.
When you mark logic with use cache, Next.js treats the function or component result as cacheable server work. That cached result can survive multiple requests in development and production depending on how the framework is managing its cache lifecycle.
So when the user performs a hard refresh:
- The browser sends a new request.
- Next.js receives that request.
- Next.js can still return the previously cached server result from use cache.
This is why timestamps, random values, or date outputs appear stuck even though the page was fully refreshed in the browser.
Another important detail: use cache is not designed to be invalidated by browser actions. It is part of the server rendering model. To change its behavior, you must use explicit invalidation strategies such as cache tags, revalidation, or remove caching for values that must always be fresh.
Step-by-Step Solution
The correct fix depends on what you want:
- If the value should always be fresh, do not cache it.
- If the value should be cached but manually purgeable, use tag-based invalidation or revalidation APIs.
- If the page is for debugging, temporarily disable caching to verify behavior.
1. Remove use cache for always-changing values
If you are rendering a timestamp, current date, request-specific value, or any value that must change on every request, the simplest solution is to stop caching it.
async function getCurrentDate() {
return new Date().toISOString()
}
export default async function Page() {
const date = await getCurrentDate()
return <div>{date}</div>
}
This ensures the server computes a fresh value on every request.
2. Use cache tags if you want controlled invalidation
If you do want caching, attach a tag and invalidate it explicitly. This is the right mental model when using server caching in Next.js.
import { cacheTag } from 'next/cache'
async function getCachedDate() {
'use cache'
cacheTag('dates')
return new Date().toISOString()
}
export default async function Page() {
const date = await getCachedDate()
return <div>{date}</div>
}
Then create a server action or route handler that purges the tag:
import { revalidateTag } from 'next/cache'
export async function POST() {
revalidateTag('dates')
return Response.json({ revalidated: true })
}
Now the cached value changes only when you explicitly invalidate the dates tag.
3. Trigger invalidation from the UI instead of relying on hard refresh
Because hard refresh does not control the server cache, expose a real purge action.
'use client'
export default function RefreshButton() {
async function clearCache() {
await fetch('/api/revalidate-dates', {
method: 'POST'
})
window.location.reload()
}
return <button onClick={clearCache}>Clear cached date</button>
}
This approach matches how use cache actually works.
4. If the page must always render dynamically, opt out of static-style caching
For route-level scenarios where cached rendering is undesirable, force dynamic behavior.
export const dynamic = 'force-dynamic'
export default async function Page() {
const date = new Date().toISOString()
return <div>{date}</div>
}
This is useful when the page should never serve reused server results.
5. Validate the fix in development correctly
Development mode can be confusing because multiple cache layers may appear active during local iteration. Test with these expectations:
- A browser hard refresh should fetch the route again.
- It should not be expected to clear use cache entries.
- Only explicit invalidation, removing caching, or dynamic rendering should change the value.
# start dev server
npm run dev
# load the page
# observe cached timestamp
# hard refresh the browser
# timestamp remains the same if use cache is still active
# call invalidation endpoint
# reload page and confirm the timestamp updates
Common Edge Cases
- Mixing browser cache assumptions with server cache behavior
A hard refresh may invalidate client-side fetched assets, but server-rendered cached functions remain intact. - Caching non-deterministic values
Using use cache around new Date(), Math.random(), or request-specific logic often creates confusing output because those values become intentionally stable. - Expecting dev mode to behave like a no-cache environment
Development is not the same as disabling all framework caching. Some Next.js caching primitives still apply for correctness and feature testing. - Forgetting explicit invalidation
If you add cacheTag but never call revalidateTag, the value will continue to look stale. - Using cached data for user-specific responses
Do not cache values that depend on cookies, headers, session identity, or per-request authorization unless you are intentionally partitioning that data. - Confusing route refresh with cache purge
Calling client navigation helpers or reloading the page refreshes rendering, but does not automatically delete server cache entries.
FAQ
Does browser hard refresh ever clear use cache data?
No. use cache is part of Next.js server-side caching, so browser refresh behavior does not directly purge it.
What should I use if I want manual cache clearing?
Use cache tags with revalidateTag, or another explicit revalidation mechanism provided by Next.js. That gives you deterministic invalidation instead of relying on browser actions.
Why does this feel surprising with timestamps or dates?
Because dates look like values that should change on every refresh. Once wrapped in use cache, they stop being request-fresh values and become cached server results, which makes the timestamp appear frozen.
The practical takeaway is simple: hard refresh affects the browser, not the Next.js server cache. If you need fresh values, remove use cache. If you need controlled reuse, keep use cache and add explicit invalidation with tags or dynamic rendering rules.