Draft Mode and Token Authentication

Implementing secure draft previews requires strict route isolation and cryptographic token validation. Headless Content Management Systems (CMS) expose unpublished content through dedicated endpoints. Without proper gating, these routes leak sensitive data to production caches. This guide details the architecture for token-scoped preview sessions, framework routing, and edge cache management.

Secure Preview Architecture

Route isolation boundaries prevent draft payloads from bleeding into production environments. You must intercept incoming requests before they reach the origin server. Map your token injection flow according to established Preview & Draft Workflow Implementation standards. Configure edge cache bypass rules to skip Content Delivery Network (CDN) storage when valid authentication headers are detected.

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyToken } from '@/lib/auth';

export function middleware(req: NextRequest) {
 const token = req.nextUrl.searchParams.get('token');
 
 // Critical: Validate signature before proceeding
 if (!token || !verifyToken(token)) {
 return NextResponse.rewrite(new URL('/401', req.url));
 }
 
 // Bypass static cache for authenticated requests
 const response = NextResponse.next();
 response.headers.set('Cache-Control', 'no-store, private');
 return response;
}

The middleware intercepts query parameters and validates the cryptographic signature. Invalid tokens trigger a secure rewrite. Valid tokens attach no-store directives to force the edge layer to fetch fresh content directly from the origin.

Framework-Specific Routing & State Management

Server-side draft mode APIs toggle rendering contexts without exposing credentials to the browser. Apply the Setting Up Live Preview in Next.js patterns to scope sessions to httpOnly cookies. Never pass raw tokens to client-side JavaScript. Validate signatures on the server before activating draft state.

// app/api/draft/route.ts
import { draftMode } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
import { validateToken } from '@/lib/auth';

export async function GET(req: NextRequest) {
 const { searchParams } = new URL(req.url);
 const token = searchParams.get('token');
 const slug = searchParams.get('slug');

 if (!validateToken(token)) {
 return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
 }

 // Critical: Enable draft mode and set secure cookies
 draftMode().enable();
 return NextResponse.redirect(new URL(slug || '/', req.url));
}

This handler verifies the token and activates Incremental Static Regeneration (ISR) draft mode. The framework automatically attaches session cookies. Subsequent requests bypass static generation and fetch live draft data through dynamic server-side routing.

Real-Time Sync & Webhook Integration

Draft tokens must expire quickly to prevent unauthorized access to unpublished content. Automate token regeneration when your CMS triggers a save event. Integrate Webhook Triggers for Real-Time Updates to purge stale preview caches immediately after payload delivery. Scope JSON Web Token (JWT) claims to specific document identifiers with strict expiration windows.

// lib/webhook-handler.ts
import jwt from 'jsonwebtoken';

export function generatePreviewToken(docId: string) {
 const payload = {
 scope: 'preview',
 docId,
 // Critical: 5-minute expiration limits exposure window
 exp: Math.floor(Date.now() / 1000) + 300,
 };

 return jwt.sign(payload, process.env.PREVIEW_SECRET!, { algorithm: 'HS256' });
}

The function signs a payload containing the document scope and a strict timestamp. The CMS calls this endpoint on every draft save. The resulting token routes directly to the preview handler. Expired tokens fail validation automatically.

CMS-Specific Security Hardening

Restrict Application Programming Interface (API) query scopes to authenticated draft requests only. Follow the Securing draft endpoints with JWT in Sanity guidelines to enforce zero-trust architectures. Implement rate limiting and automated token rotation to prevent unauthorized scraping.

# cms-token-config.yaml
api:
 token: "sk_draft_preview"
 scope: "preview"
 permissions:
 - read: "drafts"
 - read: "published"
 rate_limit: 100/min
 rotation_policy: "on_save"

This configuration isolates draft permissions from production API keys. Rate limits prevent brute-force token enumeration. Rotation policies invalidate old credentials immediately after content updates.

Pitfalls

  • Token Leakage in URLs: Query parameters persist in browser history and server logs. Always exchange URL tokens for httpOnly cookies immediately after validation.
  • Cache Poisoning: Missing Vary headers cause cached draft content to serve to public users. Attach Vary: Cookie to all preview responses.
  • Stale ISR Builds: Draft mode does not automatically clear static pages. Trigger manual revalidation or use draftMode().enable() to force dynamic rendering for the session.

Developer Experience (DX) Tradeoffs

  • Security vs Performance: Short-lived tokens prevent unauthorized access but increase CMS-to-edge latency during active editing. Mitigate by caching validated tokens at the edge for 5โ€“10 minutes.
  • Client vs Server Validation: Client-side token injection risks exposure in browser developer tools. Server-Side Rendering (SSR) middleware validation adds compute overhead but guarantees secure draft routing.
  • Static vs Dynamic Rendering: Bypassing CDN cache for drafts ensures content freshness but strains origin servers. Use token-scoped cache keys to isolate draft variants without full cache invalidation.

FAQ

Q: How do I handle token expiration during long editing sessions? A: Implement silent refresh logic. When the preview UI detects an expired token, request a new one via a secure background endpoint. Update the session cookie without reloading the page.

Q: Can I use draft mode with fully Static Site Generators (SSG)? A: Yes, but you must route preview requests to a dynamic serverless function. Static builds cannot evaluate tokens at request time. Use on-demand revalidation to generate draft pages dynamically.

Q: What happens if the preview secret is compromised? A: Rotate the PREVIEW_SECRET environment variable immediately. Invalidate all active sessions. Update CMS webhook configurations to use the new secret. Draft tokens signed with the old key will fail validation.