Why Headless WordPress Preview Stops Working and How to Fix It

ℹ Disclaimer: Content may contain affiliate links, WPThink.com may earn a commission from qualifying purchases.

When Preview Lies

The broken screen is usually the last place the failure began.

A click on Preview should open a draft, yet the browser lands on a 404, bounces to the homepage, spins through login again, or shows yesterday’s published version. In a headless WordPress stack, that moment feels catastrophic because the symptom looks final.

It rarely is. A bad preview experience can start in four different layers: WordPress never generated a valid preview link, authentication did not survive the handoff, the frontend failed to resolve draft mode, or a cache/CDN served old content instead of the draft response. SAME symptom, different root cause. Treating every preview failure as a routing bug is how teams lose hours.

Mental model

Trace preview as a chain of handoffs

  1. WordPress creates the preview request

    The editor click should produce a URL, token, nonce, or signed parameter that identifies a draft or revision. If that link is wrong, every later layer will look broken.

  2. The browser reaches the preview entry point

    That request lands on a frontend route, middleware handler, or API endpoint. Redirect loops, expired signatures, and missing environment variables usually fail here.

  3. The frontend establishes preview state

    Draft mode normally becomes a cookie, session flag, or temporary auth state. If this handoff fails, the site loads correctly but only serves published content.

  4. The app fetches unpublished content

    The frontend must call WordPress with the right API, credentials, and draft-aware query. Wrong IDs, missing auth headers, or revision-blind queries return stale or empty data.

  5. Rendering must bypass caches

    Even a successful draft fetch can be hidden by CDN, framework, or browser caching. The last hop is a page render that explicitly treats preview as uncacheable.

The fastest diagnosis is to test each hop in order: link, route, cookie, data request, final HTML.

Common culprit

Start with routing, not content

Most misdirected previews break before draft data is ever requested.

When preview opens the homepage, the wrong post, or a 404, the failure is usually in URL mapping rather than draft retrieval. In many headless setups, the frontend never reaches the intended resource, so changing GraphQL queries, REST filters, or revision logic fixes nothing.

What usually goes wrong

  • A preview link is built from a slug that has changed, is duplicated, or is not yet published.
  • The app tries to preview content on its public route instead of a dedicated preview entry route.
  • The CMS generates frontend URLs for the wrong environment: production domain, local host, branch deploy, or mismatched protocol.

A safer pattern is to route preview by a stable identifier such as post ID, database ID, or UUID, then resolve the draft server-side. Slugs remain useful for display, but they are poor keys for preview handoff because editors change them precisely when preview matters most.

A more reliable route design

Use a route like /api/preview?type=post&id=123&token=... or /preview/post/123. That route should validate the token, enable draft mode, look up the current slug or canonical path, and then redirect internally to the render route.

Also verify URL construction per environment:

  • frontendSiteUrl matches the active deployment
  • preview callbacks preserve protocol and base path
  • branch or staging domains are not hardcoded away
Session state

Why preview sessions vanish across domains

A preview can route correctly and still collapse the moment the frontend loads. The usual cause is not WordPress itself, but browser cookie policy colliding with cross-domain auth.

If WordPress lives on cms.example.com and the frontend on www.example.com or preview.example.app, the browser may refuse to send or keep the cookie that marks draft mode. Common triggers include:

  • cookies scoped to the wrong domain
  • SameSite=Lax or Strict on a cross-site handoff
  • missing Secure on HTTPS sites
  • Safari and Chromium privacy features shortening or blocking third-party cookies
  • serverless preview endpoints setting a cookie, then redirecting before it persists

The symptom looks like “preview worked for one request” or “redirected back to published content.” In practice, the preview token arrived, but the session did not survive the redirect chain.

Two common designs

Cookie-based draft mode feels seamless and supports multiple page loads, but it is fragile across domains, subdomains, and stricter browser settings.

Signed preview URLs put the proof in the URL itself, often with an expiry and HMAC or JWT signature. They avoid most cross-site cookie problems, but require careful TTLs, leak prevention, and cache bypass rules.

Same-site and same-origin are not the same thing

A subdomain change can be enough to break preview state. cms.example.com to www.example.com may look related, but the browser still applies cookie rules with great precision. Treat preview as an auth flow, not a simple redirect.

Fetch layer

When preview resolves to the published record

If the preview page renders correctly but shows only live content, the failure is usually not routing. The frontend reached a page; it fetched the wrong record. In headless setups, that often means a slug lookup returned the published post while the preview request needed a draft, autosave, or revision tied to a specific ID.

Check what the frontend is querying

A quick pattern check usually exposes the mismatch:

  • Slug query: convenient for public pages, but commonly resolves to the published post only.
  • ID query: better for preview, because the preview link usually carries a post ID and status context.
  • Revision query: required when the CMS stores previewable changes in an autosave or revision object rather than the main post row.

If the request is anonymous, WordPress will often hide draft-state data even when the route itself is correct. Missing or stripped Authorization headers are a common cause, especially behind proxies, edge functions, or server actions.

WordPress API specifics

With the REST API, preview often depends on authenticated requests plus fetching the parent post’s latest autosave or revision. With WPGraphQL, preview usually relies on asPreview-style behavior or a dedicated resolver that swaps the published node for its preview version.

The key test is simple: compare the record ID, status, and modified content returned in preview mode against the published API response. If those payloads match, preview is not actually querying preview data.

Content shape

When preview content arrives half-formed

A preview can load the correct record and still look broken because the frontend receives partial draft data. Missing blocks, empty custom fields, unresolved media, and vanished related items usually indicate a shape mismatch between what the renderer expects and what the API actually returns.

This is common in Gutenberg block preview problems. A block-based frontend often expects a normalized tree with parsed attributes, inner blocks, and server-rendered output for dynamic blocks. If preview fetches raw post_content but skips block parsing, render_block, or equivalent serializers, dynamic pieces such as query loops, embeds, and shortcodes disappear.

Custom fields fail differently. Many headless stacks read fields from the parent post, while the previewed values live in an autosave or revision. Field resolvers must be revision-aware; otherwise ACF, meta, or SEO fields silently fall back to published values.

Related content and media are often filtered by permissions:

  • Attachments may require authenticated URLs or transformed image data unavailable in preview mode.
  • Relationships to drafts or private posts may be excluded by capability checks.
  • Resolvers may strip null or unauthorized nodes, breaking components that assume full arrays.

A reliable fix is to compare the published payload with the preview payload, field by field, then align serializers, revision lookups, and permission rules.

In production

When preview breaks after deployment

Check the deployed path

If preview survives locally but fails in production, environment drift is the usual culprit. Compare SITE_URL, preview origin, WordPress home/site URLs, and redirect rules. A link signed for staging.example.com often fails after a forced hop to www.example.com, especially when cookies or tokens are origin-bound.

Similar production-only failures appear when teams preview WordPress drafts in Astro, because static output can hide preview-state mistakes until deployment.

Then verify what changed between build time and request time:

  • Secrets rotated in one service but not the other
  • CDN or edge caches storing preview redirects, JSON, or Set-Cookie
  • Static-first routes serving prebuilt HTML before draft mode bypasses cache

If the page updates only after a purge, application logic is usually intact; the broken piece is the cache key or cache policy.

A healthy API response can still produce a stale page

On static deployments, draft data may be fetched correctly while the edge still serves published HTML. Preview requests must force dynamic rendering and a no-store path through every cache layer.

Quick triage

Five-minute isolation run

  • Open the preview URL in a fresh private session

    If routing fails before draft mode appears, the fault is upstream of content.

  • Trace one request end to end

    Check token, hostname, route params, and redirect targets together.

  • Verify preview state, then inspect the first data fetch

    Look for auth, revision or autosave IDs, and unintended slug queries.

  • Bypass every cache layer

    Compare headers and payloads for preview versus published requests.

  • If data is right but HTML is wrong, inspect rendering

    Focus on block transforms, media permissions, and revision-aware field mapping.

Conclusion
  • Keep a single preview URL contract per environment.
  • Log the revision ID and cache status on every preview request.
  • Treat preview auth, routing, and data fetches as separate observability checkpoints.

Most preview outages become obvious once the chain is tested in order: entry, state, fetch, render. Prevent recurrence by versioning preview secrets, documenting domain boundaries, and emitting traceable headers for every preview hop.