Ryu Hi Trylotsy, I’d separate this into a few different “old version” cases, because they have different causes. First, ask one affected user to open DevTools → Network and hard reload, then check the main document request for `/` or the affected route: ``` Status: Cache-Control: x-vercel-cache: x-vercel-id: ``` If the **HTML document** itself is old, I’d check for something in front of Vercel, like Cloudflare, a corporate proxy, browser cache, or a service worker/PWA. A service worker is a common cause when only some users keep seeing an older frontend after a successful deployment. If the HTML is new but the page still behaves like the old app, check which JS chunks are being loaded: ``` Network tab → filter "_next/static" ``` `/_next/static/...` assets are normally content-hashed and cached aggressively. That is expected. A new deployment should usually reference new chunk filenames from the new HTML. I would not try to manually purge `/_next/static` unless there is evidence the new HTML is still pointing at old chunk files. The Data Cache is a different layer. It can explain stale fetched data or ISR/static page content, but it usually would not explain old client-side code or an old UI bundle by itself. A few checks I’d run: ``` curl -I https://yourdomain.com/ curl -I https://yourdomain.com/?cache-bust=$(date +%s) ``` Then compare the response headers with what an affected user sees. If your curl gets the new deployment but the affected browser gets old HTML, I’d look at browser/service-worker/downstream cache. If both get old HTML, then check whether the production alias is actually pointing at the latest deployment. Vercel’s CDN cache docs are useful for the static asset behavior: https://vercel.com/docs/caching/cdn-cache One useful follow-up detail would be whether affected users see the new version after unregistering any service worker or opening the site in a private/incognito window.
Trylotsy Thanks for the response. The challenge is that this is affecting a consumer-facing application with a large mobile user base, so it isn't practical to walk individual users through DevTools checks, cache clearing, service worker inspection, or other device-specific troubleshooting steps. What we're seeing is inconsistent behavior across devices: * Some users see the latest version on desktop but not mobile. * Some see the latest version on mobile but not desktop. * Some see the latest version on both. * Some continue to see the old version on both. Because the issue appears random across users and devices, I'm trying to determine whether there is a deployment-side solution that can prevent stale frontend builds from being served in the first place. A few questions: 1. Are there any known Vercel or Next.js deployment configurations that can cause older HTML documents to remain cached after a successful production deployment? 2. Is there a recommended way to force all users onto the latest build without requiring manual intervention on their devices? 3. If service workers are a common cause, what is the recommended strategy for ensuring outdated service workers are automatically replaced during deployment? 4. Has Vercel replaced the old Data Cache purge workflow with another mechanism for clearing cached frontend content globally? At this point I'm less interested in debugging individual devices and more interested in understanding whether there is a deployment-level fix or best practice that guarantees users receive the latest application version after a production release.
Ryu Hi Trylotsy, I don’t think there is a single Vercel-side purge that guarantees every already-open browser/device moves to the newest frontend bundle. For a normal Next.js deployment, the production alias should point at one deployment, and `/_next/static/...` assets are content-hashed, so old JS chunks usually only continue loading if old HTML or a client-side cache is still serving the old app shell. For the “random across users/devices” pattern, I’d focus on these deployment-level checks: 1. Check whether you have a service worker/PWA layer, such as `next-pwa`, Workbox, a custom `sw.js`, or a previous PWA config that might still be installed on users’ devices. 2. Make sure document/navigation requests are not cached with `CacheFirst` or long `StaleWhileRevalidate` rules. Those rules can keep serving an old HTML shell that references old chunks. 3. Make sure the service worker file itself is not long-lived cached. Files like `/sw.js`, `/service-worker.js`, or `/workbox-*.js` should be rechecked frequently, not cached for days. 4. Check `next.config`, `vercel.json`, middleware, and any CDN/proxy in front of Vercel for custom `Cache-Control` headers on HTML routes. I would avoid long `max-age` on app documents if you need fast rollout of new builds. A practical rollout pattern is to expose a tiny version endpoint or static `version.json` containing the deployment commit/build ID, fetch it with `cache: "no-store"` from the client, and prompt/reload when it changes. That does not require users to open DevTools, and it gives you a controlled way to move active sessions to the latest build. If you are using a service worker today, I’d first ship an update that either unregisters it if you do not need PWA behavior anymore, or uses a deliberate update flow such as `skipWaiting` / `clientsClaim` plus a reload prompt. The Data Cache is more relevant for stale fetched data or ISR content; it is not the first place I’d look for old frontend bundles across random browsers.
Swarnava Sengupta With **106M ISR writes** vs **30M ISR reads**, your write-to-read ratio is unusually high (about 3.5:1). This typically indicates one or more of these issues: 1. **Very short revalidation intervals** – If your `revalidate` value is too low (e.g., 10-30 seconds), pages regenerate frequently even if content hasn't changed 2. **Excessive on-demand revalidation calls** – If your backend triggers `revalidatePath()` or `revalidateTag()` too aggressively on every content update 3. **Bot/crawler traffic triggering regenerations** – Bots hitting many unique URLs can cause cache misses that trigger new page generations 4. **Large number of unique paths** – With 15k+ sitemap URLs, even moderate traffic patterns can lead to significant write volume **Recommendations to Reduce ISR Writes** * **Increase your `revalidate` interval:** If your content doesn't need minute-by-minute freshness, consider increasing from something like `revalidate = 60` to `revalidate = 3600` (1 hour) or higher. This alone can dramatically reduce writes. * **Use on-demand revalidation strategically:** Instead of relying solely on time-based revalidation, use `revalidatePath()` or `revalidateTag()` only when content actually changes. This lets you set longer time-based intervals as a fallback. * **Audit your revalidation triggers:** If your backend sends webhooks on every CMS update, ensure you're only revalidating the specific paths that changed, not entire route segments. * **Consider static generation for stable content:** Pages that rarely change (like archived listings) could use `revalidate = false` to generate once and never regenerate. * **Batch revalidations:** If multiple content items update simultaneously, use tag-based revalidation to invalidate related content in one operation rather than many individual path revalidations. **Quick Wins to Investigate** 1. Check your `revalidate` values across your property listing pages – are they all using the same low interval? 2. Review any API routes or server actions calling `revalidatePath()` – are they being triggered more often than needed? 3. Look at your analytics for bot traffic patterns that might be hitting many unique listing URLs
Akash Electron thank you @swarnava
Pauline P. Narvas Spot on! Your assumption is exactly right. :D Since that Hero component with `revalidate` is shared across your `generateStaticParams` pages, each one of those 1,350+ pages effectively "inherits" its own ISR schedule. This means that after the 1-week window, a visit to any page will kick off a background regeneration. If your traffic hits all those pages, you're looking at a huge spike in serverless executions which is definitely why you're seeing those limits pop up! **A couple ways we can optimize this:** **Fetch client-side:** This is the most straightforward path. If you move the GitHub stars fetch to a client-side call in your Hero component, your pages stay purely static and you stop the ISR "cascade." **Create a dedicated API route:** You can wrap the fetch in a route like `/api/github-stars` and handle the caching there. This keeps the logic centralized: ``` export async function GET() { const stars = await fetch('https://api.github.com/repos/...'); return Response.json(data, { headers: { 'Cache-Control': 's-maxage=604800, stale-while-revalidate' } }); } ``` Hope that helps! Let us know how you get on!
Juan D. Martinez I was thinking of going client side or creating a dedicated landing page for this hero section. I'm not a big fan of client fetching on stuff that doesn't change often, but haven't thought of that API apporach you mentioned, guess I'll try that one instead. I appreciate the help @pawlean 🙌🏻
Pauline P. Narvas No worries, Juan!
Come back with updates - excited to hear how you get on ![]()