---
title: "Revalidation"
description: "What each save revalidates, skipping it in scripts, and manual revalidation."
url: https://docs.systhema.app/payload/frontend/revalidation
version: unreleased (main)
docs_index: https://docs.systhema.app/llms.txt
---

Systhema wires Next.js cache revalidation hooks so editor changes appear without a manual rebuild. The triggers:

- **Header or Footer global saved** (published) → flushes the `/` layout cache, so every page revalidates on next request.
- **General Settings global saved** → tab-aware:
  - **Pages**, **SEO**, or **Cookie Consent** tab → flush the `/` layout cache (every page revalidates — SEO defaults affect every page's metadata).
  - **Emails** tab → no revalidation (email settings don't affect page renders).
- **Page saved or deleted** → revalidates that page's own path; if it is the currently configured homepage, `/` is revalidated too.
- **Form saved or deleted** (from `@payloadcms/plugin-form-builder`) → revalidates only the published Pages that embed that form via a form block — not the whole site. Draft-only saves are skipped.
- **Upload's alt text changed** → flushes the `/` layout cache. An image can appear anywhere (hero backgrounds, blocks, inline lexical nodes), so which pages embed it isn't reliably queryable — a site-wide flush guarantees every page showing the image picks up the new alt.

This is a behaviour change from versions that flushed on every `general-settings` save. If you have custom `afterChange` hooks on a global, check whether they should be restricted to specific tabs.

Posts, categories, tags and authors have their own fan-out; see [Revalidation (ISR freshness)](https://docs.systhema.app/payload/posts/roles-and-freshness.md#revalidation-isr-freshness). With the Cloudflare integration on, every revalidation also purges the edge cache; see [Auto-purge](https://docs.systhema.app/payload/cloudflare/purging.md).

## Localized sites

Localized hooks invalidate the internal catch-all route, not the displayed public pathname. `contentLocaleInternalPath()` produces `/<locale><pathname>` for exact-one-locale, `as-needed`, `always`, and domain routing, while locale-off keeps the original pathname. Keep `contentLocalePath()` and absolute public-href helpers for rendered links, canonical metadata, and sitemaps; an unprefixed public path is not a safe locale-specific revalidation key. (`contentLocalePath` / `contentLocaleInternalPath` are `@systhemaui/payload/next`'s content-locale-scoped wrappers around `@systhemaui/core`'s `getSysthemaLocalizedPathname` / `getSysthemaInternalLocalePathname` — see [Frontend locales](https://docs.systhema.app/nextjs/locales.md).)

## Skipping revalidation in scripts (`context.disableRevalidate`)

`revalidatePath()` only works inside a Next.js request — called from a seed script, a migration, or any other Local API program it throws `Invariant: static generation store missing`. Because `afterChange` runs inside Payload's transaction, that throw **rolls the write back**: the script reports success and the database is unchanged.

So every write from a script should carry the opt-out:

```ts
await payload.update({
  collection: 'forms',
  id,
  data,
  overrideAccess: true,
  context: { disableRevalidate: true },
})
```

**Every** Systhema revalidation hook honours it — pages, posts, categories, tags, components, forms, redirects, users, uploads, and the globals. (Before 1.7, the forms, redirects, components and upload hooks did not, so seeding those collections needed an error-swallowing wrapper. Drop the wrapper and pass the context instead.) Since a script's writes never revalidate, finish by calling [`POST /sys/revalidate`](#manual-revalidation) with `{ "paths": "*" }`.

## Manual revalidation

For the "something looks stale, refresh it now" case there are two on-demand entry points, both backed by the same internal `revalidateAllPublishedPages` helper (enumerate published Pages → revalidate each path, plus `/` when a homepage is configured):

- **`POST /sys/revalidate`** — machine-facing, authenticated with the `SYSTHEMA_API_SECRET` bearer token (for scripts and cron). Body `{ "paths": "*" }` revalidates every published page; `{ "paths": ["/a", "/b"] }` targets specific paths. Returns `{ revalidated, count }`.
- **General Settings → Troubleshoot → "Revalidate all pages"** — the same `paths: "*"` behaviour, gated on a logged-in admin session instead of the API secret. See [Troubleshoot tab](https://docs.systhema.app/payload/content/general-settings.md#troubleshoot-tab).
