How does a draft-preview mode let an editor see unpublished content on a route that every visitor otherwise gets prerendered?
answer
- make the render read something only they carry
- unguessable flag on the editor's request
- private and non-storable response
- never write back into the stored copy
- exiting must drop client-held drafts too
basics
~20 sAn unguessable flag on the editor's request is request-bound input: reading it forces a fresh render, tells the data layer to return drafts, and bypasses stored copies. That response must never be storable by a shared cache.
solid answer
~50 sPreview is the deliberate use of the static-versus-dynamic rule. An entry point verifies the editor and sets an unguessable, tamper-evident flag on their browser; afterwards their requests carry it, the render reads it, and that read is request-bound — so their request renders on demand instead of being served the stored copy. Inside that render, the data layer is told to return drafts rather than published content. Three rules keep it safe: the preview response must be non-storable and private, so no shared cache or CDN keyed on URL alone can hand a draft to a visitor; the preview render must never write into the stored copy that everyone else is served; and exiting must clear the flag and discard the client-side copies fetched while previewing, or the editor keeps seeing drafts afterwards. Frameworks differ in whether that flag read leaves the public route prerendered.
go deeper
Understand the idea: preview works because the editor's request carries something ordinary visitors do not, and reading it forces a fresh render instead of the stored page.
Walk the flow end to end — authenticate, set an unguessable flag, redirect, read it during render, switch the data source to drafts, clear it on exit.
Demonstrate the failure modes: a shared cache storing the preview response, a preview render poisoning the stored copy, and client-held draft data outliving the flag.
Treat it as controlled exposure of unpublished content: scope and expire the flag, make preview state visible, and test the negative case against the real caching path rather than locally.
## Preview is the rule used on purpose Everything else about static-versus-dynamic is about avoiding an accidental flip to per-request rendering. Draft preview is the case where you *want* it: one class of visitor — an editor checking unpublished work — must get a render that nobody else gets, on a route that is otherwise a stored file served to everyone. The mechanism follows directly from the rule: **make the render read something only the editor's request carries.** ## The shape of the mechanism 1. **An entry point authenticates the editor.** It is a route whose job is to verify that this person may preview, typically together with a token identifying what they want to look at. 2. **It sets an unguessable, tamper-evident flag** on the browser — in practice a cookie the server can verify and the client cannot forge. How that value is signed and which cookie attributes it carries belongs to the cookie layer, not here. 3. **It redirects to the real content route.** From now on the editor's requests carry the flag. 4. **The render reads the flag.** That read is request-bound input, so this request cannot be answered from a stored copy — it renders on demand. 5. **The data layer switches source.** With preview active, reads return draft or unpublished records instead of published ones, and stored data copies are bypassed rather than refreshed. 6. **An exit route clears the flag**, and the editor's next request is an ordinary visitor request again. ## The three rules that keep it safe - **The preview response must not be storable by anything shared.** Send it as private and non-storable so no intermediary, CDN or shared cache retains it. A shared cache keyed on the URL alone would otherwise serve one editor's draft to the public — the worst outcome this feature can produce. - **A preview render must not become the stored copy.** If preview rendering writes back into the page store or the data store used for regeneration, a single editor's visit poisons what every visitor receives. Preview must read around the stored copies without replacing them. - **Exiting must clear more than the flag.** Draft data fetched while previewing may sit in the browser's own in-memory or persisted client-side stores. Dropping the flag stops future renders from returning drafts, but stale draft payloads already held by the client can keep showing until those copies are discarded too. | Concern | What goes wrong if ignored | |---|---| | Shared-cache storage of the response | Unpublished content served to the public | | Preview writing into stored copies | Every visitor gets the draft render | | Guessable flag value | Anyone can read unpublished content | | Exit clears the flag only | The editor keeps seeing drafts from client-held data | | No expiry on the flag | A forgotten browser stays in preview indefinitely | ## Where frameworks genuinely differ The interesting subtlety is what reading the flag does to the *public* route. In a framework that infers rendering mode from what the render touched, reading the flag is an ordinary request-bound read, so a naive implementation reclassifies the route as per-request **for everyone** — the feature that was meant to affect one editor quietly removes prerendering for the whole site. Other frameworks special-case the preview flag so the route stays prerendered and only requests carrying the flag bypass the stored copy. This is a real divergence, not a detail: check which behaviour you get before assuming the public route is unaffected. The usual mitigation in the first kind of framework is to check the flag somewhere that is not the page render — a request-time layer in front of the route, which can route flagged requests to a fresh render while everyone else continues to hit the stored output. ## Operating it - **Scope the flag.** Preview should be limited to the content the editor was authorised for, and should expire, so a forgotten session does not become a standing door into unpublished work. - **Make preview visible.** A persistent banner saying "you are viewing drafts" prevents the support ticket where an editor reports a bug against content nobody else can see. - **Test the negative.** The test that matters is not "an editor sees the draft"; it is "a visitor without the flag, arriving immediately after the editor, gets the published page", asserted against the real caching path rather than a local dev server. - **Watch the exit path.** Verify that after exiting, a hard reload and an in-app navigation both show published content, since those take different paths through client-held data.
- Why must the preview response be marked private and non-storable?Because shared caches key on the URL and generally ignore who asked. A storable preview response can be retained and replayed to ordinary visitors, publishing unreleased content by accident. Marking it private and non-storable keeps it in the editor's browser only, which is the one place it belongs.
- An editor exits preview but still sees drafts while navigating the app. What is happening?The flag is gone, so new renders return published content, but draft payloads fetched during preview are still held in client-side data stores and are being reused for those navigations. The exit path has to invalidate those copies as well, and a full reload will usually confirm the diagnosis.
- How can preview avoid removing prerendering from the public route?Do the flag check outside the page render — in a request-time layer in front of the route — so only requests carrying the flag are diverted to a fresh render, and everyone else continues to be served the stored output. Some frameworks special-case the flag for you; confirm which behaviour yours has rather than assuming.
saying these in an interview costs you the question
- Uses a plain query parameter anyone can guess to enable preview.
- Lets the preview response be stored by a shared cache.
- Allows a preview render to overwrite the stored public copy.
- Thinks clearing the flag is enough to stop showing drafts.
- Assumes preview never affects how the public route is rendered.
- Leaves the preview flag with no expiry or content scope.