Astro + Vercel ISR: Non-obvious Gotchas
Today I was working with ISR (Incremental Static Regeneration) in Astro on Vercel and ran into several non-obvious issues. Writing this down for myself and anyone else who encounters the same.
How prerender Works
Without ISR:
prerender = true— page is generated at build time, staticprerender = false— SSR on every request
With ISR:
prerender = true— static at build time (ISR doesn’t affect it)prerender = false— SSR only once, then CDN cache
This is the key point: with ISR enabled, pages with prerender = false are built at runtime only on the first request, then served as static.
Clearing ISR Cache
Two methods:
- On-demand — HEAD request with
x-prerender-revalidate: <bypassToken>header - By TTL — automatically, if
expirationis set in config
// src/lib/revalidate.ts
await fetch(`${siteUrl}/wishlist`, {
method: "HEAD",
headers: {
"x-prerender-revalidate": bypassToken,
},
});
API Routes Are Not Excluded Automatically
This was unexpected: API routes (/api/*) also fall under ISR caching. If you don’t add them to exclude, a POST request might return a cached response from the first call.
// astro.config.mjs
adapter: vercel({
isr: {
bypassToken: VERCEL_ISR_BYPASS_TOKEN,
exclude: [/^\/api\/.*/], // required!
},
}),
Regex in exclude Must Match Exactly
I had an admin panel at /wishlist/admin. Initially, the exclude was:
exclude: [/^\/wishlist\/admin\/.*/]
But this didn’t match /wishlist/admin (without trailing slash). Fixed it to:
exclude: [/^\/wishlist\/admin(\/.*)?$/]