Persisting ISR cache across builds/deploys

I have a 1M page site with export const revalidate = 2592000. Every deployment completely invalidates my ISR cache, causing massive ISR writes + costs.

Evidence:

  1. Dec 31 had 4 deploys → 2M (!!!) ISR writes (~$8) with only 20 actual site visits on the day. Jan 1 already has 500K writes in 1 hour. I dont even have 2M pages!

  2. I tested a specific URL before/after deploy - etag changed from “dyw7c1hf012e6g” to “3ivi8uvt5k2e64” and went from HIT to MISS. The trigger is changing translation files in public/locales/*.json.

  3. Vercel analytics show 98% cache miss rate (257K misses / 272K requests).

My config: export const revalidate = 2592000, data wrapped in unstable_cache with 30-day revalidate, firewall rules active for bots. The docs say ISR cache persists across deployments, but mine clears completely every time. Is this expected? How do I prevent unnecessary ISR writes? What am I missing? My build logs show 30d revalidation but my cache still clears every time. I dont have any cache clearing configs in my vercel project settings.

Im on the pro plan. I’ve been trying to get support on this since Dec 12. I know its a busy month but I urgently need some help around this because of the extremely large number of ISR writes happening daily ballooning up my on demand costs.

Team URL: https://vercel.com/irfanahmds-projects

BUILD LOG SHOWS 30 DAY REVALIDATE

LAST 24 HRS FROM OBSERVABILITY

CURL BEFORE DEPLOYMENT (CACHE HIT - 01 Jan 2026 03:22:36 GMT)

┌[irfan@MacBookPro] [/dev/ttys000] [main :high_voltage:]

└[~/Development/Projects/brainrot-3e9ea85fc277211a5cbf721ab5c51215db5a372b]> curl -I Community Manager (Affiliate Marketing) कवर लेटर प्रारूप - LockedIn

HTTP/2 200

age: 2

cache-control: public, max-age=3600

content-security-policy: frame-ancestors ‘self’ https://www.browserbase.com

content-type: text/html; charset=utf-8

date: Thu, 01 Jan 2026 03:22:36 GMT

etag: “dyw7c1hf012e6g”

referrer-policy: no-referrer

server: Vercel

strict-transport-security: max-age=63072000

vary: rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch

x-content-type-options: nosniff

x-frame-options: ALLOW-FROM https://www.browserbase.com

x-matched-path: /[locale]/[…slug]

x-nextjs-prerender: 1

x-nextjs-stale-time: 4294967294

x-powered-by: Next.js

x-vercel-cache: HIT

x-vercel-id: yul1::iad1::jf9w9-1767237759386-435481a3e529

content-length: 152754

CURL AFTER DEPLOYMENT (CACHE MISS - 01 Jan 2026 04:40:23 GMT)

┌[irfan@MacBookPro] [/dev/ttys000] [main]

└[~/Development/Projects/brainrot-3e9ea85fc277211a5cbf721ab5c51215db5a372b]> curl -I Community Manager (Affiliate Marketing) कवर लेटर प्रारूप - LockedIn

HTTP/2 200

age: 0

cache-control: public, max-age=3600

content-security-policy: frame-ancestors ‘self’ https://www.browserbase.com

content-type: text/html; charset=utf-8

date: Thu, 01 Jan 2026 04:40:23 GMT

etag: “3ivi8uvt5k2e64”

referrer-policy: no-referrer

server: Vercel

strict-transport-security: max-age=63072000

vary: rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch

x-content-type-options: nosniff

x-frame-options: ALLOW-FROM https://www.browserbase.com

x-matched-path: /[locale]/[…slug]

x-nextjs-prerender: 1

x-nextjs-stale-time: 4294967294

x-powered-by: Next.js

x-vercel-cache: MISS

x-vercel-id: yul1::iad1::48t2f-1767242423732-fd737724899d

content-length: 152716

ISR writes only occur when content actually changes. From the optimization guide:

“When attempting to perform a revalidation, if the content has no changes from the previous version, no ISR write units will be incurred.”

But if your pages include translation data from those JSON files, every page’s output is changing, causing legitimate writes.

Another thing is to check if your build process is including timestamps, unique IDs, or other non-deterministic data in the ISR output. You can compare two HTML responses before/after deployment to see what’s different. Anything that changes will cause another ISR write