Using Vercel On‑Demand Revalidation with Tags to Keep Content Fresh Without Sacrificing Speed
Learn how to combine Incremental Static Regeneration with tag‑based on‑demand revalidation so editors can instantly refresh groups of pages while retaining edge‑cached performance.
21 Jul 2026, 15:24 UTC

The problem: fast reads vs. timely updates
Content‑heavy sites benefit from static generation because pages are served from Vercel’s edge network with low latency. However, when editors change a blog post or product listing, waiting for the next build or relying on a fixed revalidate interval can leave visitors seeing stale content for minutes or hours.
The goal is to keep the performance advantages of static edge caching while giving content teams a way to trigger an immediate refresh for a set of related pages.
How Incremental Static Regeneration works on Vercel
In a Next.js page, getStaticProps can return a revalidate value (in seconds). Vercel treats the generated HTML as a stale‑while‑revalidate cache entry at the edge:
- The first request after a deployment builds the page and stores it in the edge cache.
- Subsequent requests within the
revalidatewindow receive the cached version instantly. - When the window expires, the next request triggers a background regeneration; while that rebuild runs, the stale version is still served, keeping the site available.
This gives near‑static performance with eventual freshness, but the refresh timing is still tied to the interval.
Adding on‑demand revalidation with tags
Vercel exposes a serverless API route that can call res.revalidate(path) (or res.revalidate(tag)) to force a regeneration outside of the interval. By tagging pages during generation, a single request can refresh many pages at once.
Key steps:
- In
getStaticProps, return arevalidateinterval (e.g., 60 seconds) and atagunder theunstable_tagsfield (Next.js 13+). - Create an API route (
pages/api/revalidate.ts) that verifies a secret token, then callsres.revalidatewith the tag. - Secure the route: reject requests lacking the correct header or token, and optionally rate‑limit.
- When your CMS or webhook fires, send a POST to the API route with the tag; Vercel will queue regeneration for every page that carries that tag.
Worked example
Suppose you have a blog where each post is a static page at /posts/[slug]. You want editors to be able to refresh all posts when a new author is added.
// pages/posts/[slug].tsx
import type { GetStaticProps } from 'next'
export const getStaticProps: GetStaticProps = async ({ params }) => {
const post = await fetchPost(params.slug)
return {
props: { post },
// Revalidate at most once per minute if no on‑demand trigger
revalidate: 60,
// Tag all posts with "blog-posts"
unstable_tags: ['blog-posts'],
}
}
export default function Post({ post }) {
return (
{post.title}
)
}
// pages/api/revalidate.ts
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const token = req.headers['x-revalidate-token']
if (token !== process.env.REVALIDATE_SECRET) {
return res.status(401).json({ message: 'Invalid token' })
}
const { tag } = req.body
if (!tag || typeof tag !== 'string') {
return res.status(400).json({ message: 'Missing tag' })
}
try {
// This tells Vercel to regenerate all pages with the given tag
await res.revalidate(tag)
return res.status(200).json({ revalidated: true, tag })
} catch (err) {
return res.status(500).json({ error: 'Revalidation failed' })
}
}
To trigger a refresh from your CMS:
curl -X POST https://your-site.vercel.app/api/revalidate \
-H "Content-Type: application/json" \
-H "x-revalidate-token: $REVALIDATE_SECRET" \
-d '{"tag":"blog-posts"}'
After the request, Vercel begins rebuilding the tagged pages in the background. Requests that arrive while the rebuild is in progress still receive the previously cached version; once the new build finishes, the edge cache is updated and subsequent visitors see the fresh content.
Trade‑offs and limitations
- Latency during regeneration: Users may see stale content for the duration of the rebuild (typically a few seconds to a couple of minutes, depending on page complexity and data‑fetching time).
- Security: The revalidation endpoint must be protected. If left open, anyone could purge the cache or cause excessive builds, leading to denial‑of‑service or unexpected costs.
- Runtime differences: Tag‑based revalidation is available in the Node.js runtime; Edge runtime currently does not support
res.revalidate. Ensure your API route uses the Node.js runtime (export const config = { runtime: 'experimental-nodejs' }if needed). - Build minutes and origin load: Frequent on‑demand triggers increase the number of serverless builds and can raise your Vercel usage. Monitor the
revalidatecalls in your deployment logs and consider cooldown mechanisms or debouncing webhook events.
Verification checklist
- Deploy the example code to a Vercel preview.
- Confirm that the initial page load returns an
AgeorCache‑Controlheader indicating edge caching. - Call the revalidation API with a valid token and tag; check the response returns
200and{revalidated:true}. - After the call, request the page repeatedly and observe when the
Ageheader resets (indicating a fresh cache). - Check Vercel’s deployment logs for a “Regenerating…” entry matching the tag.
If you see the cache update after the regeneration finishes and the endpoint correctly rejects unauthorized requests, the on‑demand revalidation with tags is working as intended.
Actionable closing
Start by adding a modest revalidate interval to your static pages, then introduce a protected API route that accepts a secret token and a tag. Tag all pages that belong to a logical group (e.g., "blog-posts", "product‑listings"). Connect your CMS or CI webhook to that route, and you’ll get instant, targeted freshness without sacrificing the global speed edge caching provides. Keep an eye on build usage and adjust the interval or add debouncing if you notice excessive triggers.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.