The Best CMS for a PWA: Cacheable Headless Content
A progressive web app (PWA) is built to be fast, offline-capable, and app-like. The content inside it — articles, product info, help pages — shouldn't be hardcoded into the service worker and shipped with every deploy. It should come from a CMS your team edits freely, delivered over an API your PWA can fetch and cache. That's the headless CMS pattern, and it fits a PWA especially well because PWAs already think in terms of fetching and caching data.
This guide covers how a headless CMS pairs with a PWA and how to pick one that feeds your app cleanly.
What a PWA needs from a CMS
A PWA has a specific content situation:
- Content over an API — the PWA fetches content as JSON, the same way it fetches app data.
- Cacheable responses — PWAs cache aggressively (service workers), so API responses should cache cleanly.
- Editor autonomy — content updates without shipping a new app version.
- Fast, small payloads — PWAs care about performance and offline size.
A PWA is already API-and-cache shaped. The CMS just has to serve content that fits that model.
The short answer
For a PWA, you want a headless CMS with a clean, cacheable REST API — your PWA fetches content as JSON, caches it in the service worker, and editors update it without an app release.
UnfoldCMS is a self-hosted CMS (Laravel 12 + React 19) with a REST API at /api/v1. Your PWA fetches posts, pages, and menus; the responses are cacheable; editors manage content in the admin. It self-hosts so content stays on your infrastructure. It's REST, not GraphQL — which suits a PWA's fetch-and-cache model well, since REST endpoints map cleanly to cache keys.
How a headless CMS fits a PWA
The flow mirrors how a PWA already works:
- Editors publish content in the CMS admin.
- Your PWA fetches that content from the CMS REST API.
- The service worker caches the responses for offline use and speed.
- Content updates on the server; the PWA picks it up on next fetch (or you use a cache-busting strategy).
Because a PWA already fetches and caches data, adding a content API is natural. See headless CMS architecture explained.
The API: what your PWA fetches
UnfoldCMS ships public read endpoints (no auth for published content) at /api/v1:
/api/v1/posts,/api/v1/posts/{slug}— articles./api/v1/pages/{slug}— static content pages./api/v1/categories/{slug}/posts— content by topic./api/v1/menus/{location}— navigation./api/v1/search— content search.
Each endpoint maps to a clean URL your service worker can cache by key. Responses use a consistent envelope. See CMS with a REST API.
Caching: the PWA sweet spot
PWAs live on caching, and REST endpoints cache more predictably than a single GraphQL endpoint (where every query is a POST with a different body). With UnfoldCMS's REST API:
- Each content URL (
/api/v1/posts/my-article) is a stable GET, easy to cache in the service worker with a cache-first or stale-while-revalidate strategy. - Your PWA serves cached content instantly and offline, revalidating in the background.
On the CMS side, you'll want caching in front of the API for busy apps — see headless CMS rate limiting and caching. The public read endpoints are rate-limited at 60/min by default.
Editor autonomy: no app release for content
The big win: content changes don't require shipping a new PWA version. Editors publish in the admin; the PWA fetches the update on its next sync. Role-based access control (custom roles, module and action permissions) keeps publishing safe — editors get content access, not settings. Your app-release cycle and your content-update cycle are decoupled.
Keeping cached content fresh
The tricky part of any cached PWA is knowing when to refresh. Two approaches with UnfoldCMS:
- Stale-while-revalidate — serve cached content, fetch fresh in the background, update the cache. Simple and effective for most PWAs.
- Webhook-driven cache-busting — UnfoldCMS fires HMAC-signed webhooks on publish, so you can trigger a cache invalidation or a versioned content endpoint. See headless CMS webhooks explained.
When to reconsider
A headless CMS may be more than a PWA needs if:
- Your PWA has almost no editorial content — a few static strings can just ship with the app.
- Your content is highly personalized per user in real time — that belongs in your app's own backend, not a content CMS.
- You have no capacity to run another service.
Frequently asked questions
What is the best CMS for a PWA?
A headless CMS with a clean, cacheable REST API is best for a PWA — your app fetches content as JSON and caches it in the service worker, and editors update it without an app release. UnfoldCMS fits: a REST API at /api/v1, self-hosted, with cache-friendly stable GET endpoints. REST suits a PWA's fetch-and-cache model better than GraphQL.
Why REST instead of GraphQL for a PWA?
REST endpoints are stable GET URLs that map cleanly to service-worker cache keys, so a PWA can cache each content URL with a cache-first or stale-while-revalidate strategy. A single GraphQL POST endpoint is harder to cache at the network layer. UnfoldCMS is REST-only.
How does content update without a new app version?
Editors publish in the CMS admin, and the PWA fetches the update on its next sync. You keep cached content fresh with stale-while-revalidate, or trigger cache-busting from the HMAC-signed webhooks UnfoldCMS fires on publish.
Does content work offline?
Yes — because your service worker caches the CMS API responses, cached content is available offline, and revalidates when the PWA is back online.
The takeaway
A PWA needs content over a clean, cacheable API — which is exactly what a REST headless CMS provides. UnfoldCMS feeds your PWA content over /api/v1, caches predictably in the service worker, and lets editors update content without an app release. Its REST-only API is a fit, not a limitation, for a PWA's fetch-and-cache model. Skip it only if your app has almost no editorial content.
Related: Headless CMS architecture explained · Headless CMS rate limiting and caching · CMS with a REST API
Free & Open Source
Own your CMS. No subscriptions.
Unfold CMS is free to download and self-host. Built on Laravel + React, full source code included.
Share this post: