Next.js App Router + a Headless CMS: Caching That Does Not Fight Your Editors

How to combine Next.js App Router fetch caching, revalidation, and webhooks so editors see fresh content without giving up performance—or confusing your team with stale pages.

R
Raşit Apalak
4 min read

Editors publish. Visitors get fast pages. Developers sleep.

The App Router makes fetch caching a first-class concept. A headless CMS makes content changes routine. If those two ideas are not aligned, you get the worst of both worlds: either stale marketing pages that embarrass your team, or zero caching that wastes money and latency.

This post gives a practical mental model and a few patterns that work well with REST-driven CMS APIs (including ElmapiCMS).


Table of Contents


The core conflict

  • Performance wants responses served from the edge or a cache with minimal origin work.
  • Editorial workflows want the latest title, hero image, or price visible within seconds of clicking publish.

Your job is not to “disable caching.” It is to scope caching so public marketing pages stay fast while mutation paths (preview, dashboard-driven tests) stay honest.


Pick a default: static, dynamic, or hybrid

Marketing pages (home, landing, campaign URLs) are usually safe to cache aggressively if you have a path to invalidate or revalidate when content changes.

Account dashboards, carts, and authenticated views should almost always be dynamic (no-store or server-only data fetching without shared cache).

A common hybrid:

  • Static shell + cached content for public routes.
  • Dynamic for anything that depends on cookies, roles, or per-request headers.

Time-based revalidation when webhooks are not wired yet

If you are early in a project, incremental static regeneration style behavior via next.revalidate is a reasonable bridge:

  • Editors see updates within a bounded window (for example, a few minutes).
  • You still get most of the benefit of caching for anonymous traffic.

This is not as crisp as event-driven revalidation, but it is simple to reason about and avoids a forever-stale page when someone forgets to deploy.


On-demand revalidation with webhooks

For production sites, prefer on-demand revalidation driven by CMS events:

  1. Editor publishes in the CMS.
  2. CMS sends a webhook to your Next.js app (or a small worker).
  3. Your app calls revalidatePath / revalidateTag for the affected routes.

This gives editors near-real-time updates without setting global cache TTLs near zero.

ElmapiCMS supports webhooks; for a detailed implementation angle, read Next.js ISR, webhooks, and cache invalidation. The important invariant is: every successful publish path should either revalidate or target a non-cached route.


Draft and preview: never cache private data

Preview and draft flows often use cookies, preview tokens, or bypass secrets. Those requests must not populate a shared cache.

Rules of thumb:

  • Mark preview routes as dynamic and non-cacheable at the edge.
  • Fetch draft content only with server-side code that validates the token on every request.
  • Never reuse the same cache key for published and draft representations.

What to validate before launch

Before you call the integration “done,” run through this list with a non-developer editor:

  1. Publish a visible change (title or hero image) on a high-traffic page.
  2. Confirm the public site updates within the expected window (webhook-driven should be seconds; ISR should match your revalidate interval).
  3. Confirm unpublished or scheduled content does not leak on anonymous URLs.
  4. Load the page in a private window to avoid mistaking your browser cache for CDN behavior.

If you are building on ElmapiCMS from scratch, the Next.js + ElmapiCMS starter setup is a good companion to this caching work—get data loading correct first, then tighten the cache story.


Caching is a product decision as much as a technical one. Write down the SLA you are promising editors (“changes visible within …”), implement the smallest mechanism that meets it, and only then optimize further.

Share this post:

Related posts