Making Previews Work in Headless WordPress
Preview is the feature editors miss most in a headless build. How the flow works, how to secure it, and what to do about revisions and unsaved changes.

In a traditional WordPress theme, preview just works. The editor clicks Preview, WordPress renders the draft through the same templates, done. Nobody thinks about it because there's nothing to think about.
Going headless breaks that completely, and the replacement has to be built. It's the single most common gap in headless projects, and it's worth understanding before you scope one.
Why it breaks
A draft doesn't exist publicly. The REST API won't return it to an unauthenticated request, and your frontend — which fetches content anonymously at build time — has no way to see it.
Worse, your frontend is probably serving static pages from a cache. Even if it could fetch the draft, the page it serves is a pre-built file from the last build.
So preview needs three things that ordinary rendering doesn't: authenticated fetching, a way to signal 'this request is a preview', and a rendering path that bypasses the cache entirely.
The flow
- The editor clicks Preview in WordPress.
- WordPress redirects them to your frontend rather than to its own preview URL, carrying the post ID, the post type and a token.
- A route on the frontend validates the token, and if it's good, enters preview mode — typically by setting a short-lived cookie.
- While in preview mode, the frontend fetches content from WordPress with credentials, requesting the draft rather than the published version.
- The page renders server-side on every request, with no caching, so each reload shows the current draft.
- An exit route clears the cookie and returns the editor to normal browsing.
Securing the preview route
This is where preview implementations most often go wrong. An unprotected preview route that accepts any post ID lets anyone read every draft on the site — unreleased announcements, embargoed content, work in progress.
- Sign the token, so it can't be forged. A shared secret and an HMAC over the post ID and an expiry is enough.
- Give it a short expiry. A preview link that works forever is a leaked draft waiting to happen.
- Bind it to the specific post. A token valid for any post is barely better than none.
- Keep the secret in environment configuration on both sides, never in the repository.
- Rate limit the route, because it's a public endpoint that triggers authenticated backend requests.
- Make sure preview pages are noindexed, so a shared link can't end up in search results.
Fetching the draft
In preview mode, the frontend needs credentials to read unpublished content. Application passwords on a dedicated account with only the capability to read drafts is the cleanest approach — it's in core, revocable, and scoped.
Request the specific revision or draft rather than the published post. The REST API can return a post in draft status when authenticated, and revisions have their own endpoints if you need a specific one.
Handle the case where the post doesn't exist, was deleted, or was published since the link was generated. Falling back to the published version with a note is friendlier than an error page.
Bypassing the cache
A preview page must never be cached, at any layer, or an editor will see stale content and conclude preview is broken.
That means server rendering on every request while in preview mode, cache-control headers that prevent browser and CDN caching, and an exclusion at your CDN for the preview cookie or route.
It's also worth making preview visually distinct — a banner saying 'Preview mode' with an exit link. Editors otherwise forget they're in it and report bugs about a page that looks out of date, which is the published version they're now bypassing.
The limits worth stating up front
- Unsaved changes usually can't be previewed. WordPress's autosave writes a revision, so preview shows the last autosave — close but not identical to what's on screen.
- Live preview as you type, which some editors expect from other CMSs, is a substantially larger piece of work.
- Preview of a whole page composed of blocks needs your frontend to render every block type. A block the frontend doesn't know about will render as nothing.
- Scheduled posts and password-protected posts each need their own handling.
- Say all of this during scoping. Editors who know the limits are fine; editors who expected WordPress behaviour and got less are not.
Testing it
- Preview a brand new draft that has never been published.
- Preview an update to an already-published post, and confirm you see the draft rather than the live version.
- Preview, edit, save, and reload — the change should appear.
- Exit preview and confirm you see the published version again.
- Try a preview link in a private window without the cookie, and confirm the token validation actually works.
- Try an expired token and a token for a different post, and confirm both are rejected.
- Test as an editor, not as an administrator. Capability differences show up here.
Budget for it
Preview is not a nice-to-have. It's the thing editors use dozens of times a day, and a headless site without it is one where nobody can check their work before publishing.
It also tends to be the last thing built and the first thing cut when a project runs late, which is exactly backwards — it's the feature the people using the site every day will judge it by.
If you're scoping a headless project, put preview in the estimate explicitly rather than folding it into 'frontend'.
Frequently asked questions
Why doesn't preview work on my headless WordPress site?
Because drafts aren't public and your frontend fetches anonymously, usually from a static build. Preview has to be built: a signed link from WordPress to your frontend, a route that validates it and enters preview mode, authenticated fetching of the draft, and rendering with caching disabled.
How do I secure a headless preview route?
Sign the token with a shared secret so it can't be forged, bind it to the specific post, and give it a short expiry. Keep the secret in environment configuration on both sides, rate limit the route, and noindex preview pages so a shared link can't reach search results.
Can editors preview unsaved changes in headless WordPress?
Generally not. WordPress's autosave writes a revision, so preview shows the most recent autosave rather than exactly what's on screen. Live preview as you type is a much larger piece of work. Say this during scoping — editors are fine with the limit if they know about it.
Which authentication should preview use?
Application passwords on a dedicated account with only the capability to read drafts. They're in core, revocable independently, and scoped to one integration. Avoid using an administrator account's credentials, and never put them anywhere the client can read them.
How much work is headless preview to build?
More than the demo suggests and less than a rebuild — but it's a discrete piece of work involving the WordPress filters, a signed token scheme, an authenticated fetch path and cache bypassing. Put it in the estimate explicitly rather than folding it into 'frontend', because it's the feature editors judge the site by.
Topics
- headless WordPress preview
- Next.js preview mode
- WordPress draft preview
- headless CMS preview