Tevpro insights
Next.js ISR on Cloudflare: On-Demand Revalidation With Sanity, D1, and R2
How Tevpro uses signed Sanity webhooks, Next.js on-demand revalidation, Cloudflare D1 tag caching, R2 incremental caching, and OpenNext to keep CMS content fresh without a redeploy.

On a modern marketing site, editors expect one thing when they click Publish: the change should appear quickly. Engineering has another priority: pages should remain aggressively cached, fast, and resilient.
Delivering both requires more than simply connecting a headless CMS to a frontend.
For Tevpro’s Sanity and Next.js website running on Cloudflare, we built an on-demand revalidation architecture that keeps content fresh without putting the CMS in the visitor request path or rebuilding the entire site every time an editor makes a change.
The architecture uses a small but deliberate chain:
When content changes, Sanity sends a signed webhook. A Next.js route verifies the request and determines exactly which cache tags and routes are affected. Cloudflare D1 provides durable tag-cache state across Workers, while Cloudflare R2 backs the incremental cache containing rendered output. OpenNext provides the bridge that allows the Next.js caching model to operate on Cloudflare Workers.
The result is a fast, cached website that can respond intelligently to publishing events, without turning every content update into a deployment.
The Problem: Publishing and Caching Are Different Jobs
There are two easy ways to keep a CMS-driven website current. Neither is particularly attractive at scale.
The first, is to make every page dynamic and query Sanity on every request. The content stays current, but the CMS becomes part of the visitor request path. That adds latency, increases dependency on an external service, and gives up many of the performance and resilience advantages of prerendering and caching.
The other extreme is a fully static website that changes only when the application is rebuilt and deployed. Performance can be excellent, but even a small editorial correction effectively becomes a software release.
For a modern marketing website, neither operating model is ideal.
Next.js Incremental Static Regeneration (ISR) provides a middle ground. The application can continue serving cached or prerendered output while selectively invalidating content when something changes.
Our implementation principle was simple:
A content change should invalidate only what needs to change—not purge the entire cache or trigger a full application deployment.
The Architecture
Each component has a clearly defined responsibility.
- Sanity remains the editorial source of truth for pages, services, posts, case studies, FAQs, legal content, people, categories, redirects, and site settings.
- Sanity webhooks provide the signal that relevant content has changed.
- Next.js verifies that signal and determines which cache tags and routes should be invalidated.
- Cloudflare D1 provides durable, shared state for the Next.js tag cache across distributed Worker execution.
- Cloudflare R2 backs the OpenNext incremental cache containing reusable page and data artifacts.
- Cloudflare Workers execute the application at the edge.
- OpenNext provides the compatibility layer that allows the Next.js caching model to run within Cloudflare's infrastructure.
The important part is not simply the technologies involved. It is the separation of responsibilities between them.
Why the Webhook Becomes the Control Plane
The CMS already knows when content changes. Instead of polling Sanity, shortening cache lifetimes, or asking every visitor request whether something has changed, we allow the publishing event itself to initiate the update process.
When an editor publishes or modifies relevant content, Sanity sends a signed webhook to a dedicated Next.js revalidation endpoint.
That endpoint is intentionally not a public "clear cache" button.
Before performing any cache operation, the route verifies the Sanity webhook signature using the configured secret. Invalid requests are rejected before they can initiate revalidation.
This creates an important security boundary. Cache invalidation is operationally powerful, so the system treats publishing as a privileged event: authenticated, verified, and narrowly scoped.
Once the event is validated, the route examines the payload and determines what actually needs to change.
Targeted Invalidation Instead of Global Purges
A simple implementation might respond to every publish event by clearing everything.
That works. It also eliminates much of the value of intelligent caching.
Instead, our revalidation route evaluates the changed document's type, current slug, previous slug, explicit tags, and associated routes.
A blog post update, for example, may affect:
- The post's canonical detail route
- The blog index
- Category or related-content listings
- Shared content associated with the post
A settings update may affect global site elements across many routes. An author update may change attribution across several articles. A category change can affect listings beyond the document that triggered the event.
This is where cache tags become particularly useful.
Cache Tags represent shared content dependencies, while path invalidation handles route-specific output. Using both gives the application a more precise way to determine what should become stale.
Where Cache Invalidation Gets Complicated
The easy case is changing a paragraph in an article.
Production systems become more interesting when content relationships start changing.
Consider a blog post whose slug changes from:
Refreshing only the new URL is not enough. The previous route may still have cached output, internal references may have changed, and redirect behavior may need to be reflected correctly.
Our revalidation logic therefore considers both the current and previous slug when processing an event.
The same principle applies to shared content.
Changing a category can affect multiple article listings. Updating an author can change information displayed across numerous posts. Updating global settings may alter navigation, footer content, metadata, or other site-wide elements.
The route maintains mappings for page-like content, services, posts, case studies, FAQs, legal pages, templates, authors, categories, settings, and redirects. Depending on the event, it can invalidate a specific route, derive canonical routes from a slug, update shared tags, or target broader route patterns when necessary.
These edge cases are why production cache invalidation requires more thought than simply calling a revalidation endpoint after every publish.
They also matter for SEO. A stale route can leave outdated metadata, internal links, page content, or redirects accessible after the underlying CMS content has changed.
Where Cloudflare D1 Fits
Cloudflare D1 serves a very specific role in this architecture.
In our OpenNext configuration, D1 is bound as the Next.js tag cache. When Next.js marks tagged content stale, that state needs to survive beyond the lifetime of an individual Worker isolate and remain available across distributed execution.
D1 provides that durable, shared state.
It is important to distinguish this from the site's content architecture.
D1 is not the content database, and it does not replace Sanity. Sanity remains responsible for structured editorial content, drafts, publishing, and content relationships.
D1 supports cache coordination.
That distinction keeps both systems focused on what they do well. Sanity manages content. D1 manages durable relational state needed by the runtime.
Software tends to behave better when every component gets to keep its day job.
Where Cloudflare R2 Fits
R2 has a different responsibility.
It backs the OpenNext incremental cache containing the page and data artifacts Next.js can reuse rather than recomputing them for every request.
Together, the two Cloudflare storage layers support different parts of the caching model:
When Sanity initiates an invalidation event, Next.js updates the appropriate cache state. The OpenNext runtime then uses that information when determining whether cached output can continue to be used or needs to be refreshed.
Keeping these responsibilities explicit makes the system easier to reason about and significantly easier to troubleshoot when something does not behave as expected.
From Publish to Fresh Content
The complete publishing flow looks like this:
- An editor publishes or updates content in Sanity.
- Sanity sends a signed webhook to the Next.js revalidation route.
- The route verifies the webhook signature.
- The application interprets the document type, tags, current slug, previous slug, and associated routes.
- Next.js marks the relevant tags and routes stale through on-demand revalidation.
- The D1-backed tag cache maintains the shared invalidation state across Workers.
- R2 continues to support the incremental-cache artifacts used by the OpenNext runtime.
- The affected content is refreshed according to the application's revalidation behavior.
The goal is not to rebuild the website every time an editor presses Publish.
The goal is to make the correct portion of the website fresh quickly while leaving everything else cached.
Designing for Failure, Not Just the Happy Path
Webhooks are integrations, and integrations can fail.
A production architecture therefore cannot assume that every publishing event will arrive exactly once, in the correct order, and without interruption.
Revalidation logic should be safe to execute more than once for the same content event. Failures should be observable, and engineering teams should have a defined recovery mechanism when an event does not complete successfully.
That is another reason we keep the invalidation process isolated from the visitor request path.
If a webhook encounters a problem, it becomes a specific integration issue that can be identified and corrected. It does not require every visitor request to become slower simply to protect against the possibility that content might have changed.
This separation improves both performance and operational resilience.
What This Architecture Gives the Team
For editors, very little changes, and that is exactly the point.
They continue working in Sanity, where content modeling, drafts, previews, and publishing workflows already live. They do not need to understand Next.js caching, Cloudflare Workers, D1, R2, or OpenNext simply to correct a sentence or publish a new case study.
For visitors, the website retains the performance characteristics of a cached Next.js application.
For engineering, publishing and application deployment become separate concerns. Routine content changes do not require a redeployment, while cache behavior remains deliberate and observable.
The architecture provides several practical benefits:
- Faster content publishing without full-site deployments
- Cached page delivery for better performance
- Reduced dependency on live CMS requests
- Targeted cache invalidation instead of global purges
- Better handling of shared content and content relationships
- More predictable behavior when URLs and slugs change
- Improved resilience between the CMS and public website
- Clearer operational boundaries for troubleshooting
The Broader Engineering Lesson
ISR is often described as a Next.js performance feature. For a content-driven production website, it is more useful to think of it as an operating model for publishing.
Editorial activity creates a verified signal. The application determines what that change affects. The caching infrastructure records the appropriate invalidation state. The runtime refreshes what needs to change while continuing to serve cached content everywhere else.
The technologies matter, but the architecture between them matters more.
In this implementation, Sanity owns content and publishing. Signed webhooks carry trusted change events. Next.js determines which tags and routes should become stale. D1 provides durable tag-cache coordination. R2 stores incremental-cache artifacts. OpenNext brings the Next.js runtime model to Cloudflare Workers.
No component is being asked to solve every problem.
That separation of responsibilities is what turns a collection of modern technologies into a production-ready system.
At Tevpro, we apply the same engineering approach to modern web applications, AI-enabled systems, enterprise integrations, and legacy modernization: understand the boundaries between systems, design for production realities, and use each technology where it provides the most value.
Building or modernizing a high-performance web platform? Tevpro engineers production-ready architectures that connect modern frontend frameworks, content platforms, APIs, cloud infrastructure, and enterprise systems.
Why work with us
Why Tevpro?
Whether you’re a startup with a bold product idea or an established company seeking a stronger delivery partner, Tevpro delivers results. Our expert consultants specialize in building secure, scalable applications that simplify operations and drive real ROI.


