Preview CMS com Next.js App Router: Server Actions, Middleware e ISR na prática
Por John Pires · 14 de março de 2026

Preview CMS com Next.js App Router: Server Actions, Middleware e ISR na prática
Contexto e motivacao
Problema prático que precisamos resolver
A necessidade de um modo de preview em sites que usam headless CMSs surge com frequência: editores querem ver alterações em rascunho (draft) antes de publicar, sem expor drafts para usuários finais e sem sacrificar o desempenho das páginas já em produção. Em um aplicativo Next.js que usa o App Router e ISR, isso envolve três responsabilidades distintas: ativar/desativar o modo preview de forma segura, garantir que o conteúdo de preview seja servido sob demanda quando apropriado, e evitar impacto no cache e nas rotas públicas.
Alternativas e trade-offs
Existem abordagens diferentes: usar a API tradicional de Preview Mode (cookies com token), rotas internas que expõem drafts via query string, ou usar o novo conjunto de utilitários do App Router (draftMode / cookies / revalidatePath). Cada alternativa tem trade-offs sobre segurança, complexidade e compatibilidade com ISR. O objetivo aqui é balancear segurança e simplicidade, aproveitando middleware para roteamento condicional e Server Actions para operações de escrita e revalidação.
Resumo dos requisitos práticos:
Ativar preview apenas para editores autenticados.
Servir rascunhos sem invalidar o cache público.
Permitir revalidação seletiva (ISR) ao publicar.
Ao projetar preview, pense em dois mundos: o público (cacheado, ISR) e o dos editores (session/cookie de preview). A separação clara evita vazamento de conteúdo e problemas de performance.
Como funciona por baixo dos panos
O papel do cookie de preview e do middleware
A técnica mais direta é marcar sessões de preview com um cookie seguro (HttpOnly, SameSite=strict idealmente). O middleware do Next.js, que roda na borda, inspeciona esse cookie em cada requisição e modifica o comportamento — por exemplo, definindo um header interno, reescrevendo para um subpath de preview ou sinalizando ao sistema de rendering para buscar conteúdo draft do CMS. Isso mantém a lógica de roteamento centralizada e performática.
ISR e revalidação: como conciliar com previews
ISR (Incremental Static Regeneration) depende de revalidação quando páginas publicadas mudam. Em Next.js App Router, fetch com a opção next: { revalidate: N } ou funções como revalidatePath/revalidateTag permitem disparar revalidação programaticamente. Quando um editor publica, você precisa revalidar as páginas públicas. Mas durante preview não deve-se forçar a revalidação do cache público — apenas servir conteúdo dinamicamente para o cookie de preview.
Conceito importante: preview = runtime rendering para usuários com cookie de preview; produção = ISR / cache para todos os outros.
Implementacao passo a passo
1) Rota de API para ativar / desativar preview
Crie handlers no App Router que setam/removem cookies de preview. Esses endpoints verificam um token (assinatura) fornecido pelo CMS ou pelo backend de autenticação antes de conceder preview. Exemplo funcional abaixo: cria um cookie seguro e redireciona de volta ao editor.
O código a seguir é o handler em /app/api/preview/route.ts. Ele valida um token simples, seta cookie HttpOnly e redireciona.
// app/api/preview/route.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export async function POST(request: NextRequest) {
const body = await request.json();
const { token, slug } = body || {};
// Contexto: valida um token gerado pelo CMS ou backend
if (!token || token !== process.env.PREVIEW_TOKEN) {
return new NextResponse('Invalid token', { status: 401 });
}
// O que faz: seta cookie de preview e redireciona para a página
const url = new URL((slug && `/posts/${slug}`) || '/', request.url);
const res = NextResponse.redirect(url);
// Cookie HttpOnly para não expor ao client-side JS
res.cookies.set({
name: 'preview_mode',
value: '1',
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 // 1 hora
});
return res;
}
O que acontece depois: o navegador recebe um cookie seguro. O middleware (abaixo) vai ler esse cookie e ativar comportamento de preview para requisições subsequentes.
2) Middleware que detecta preview e reescreve/rotas
Coloque a lógica de entrega de preview no middleware para interceptar rápido e evitar carregar rotas pesadas. O exemplo mostra como marcar a requisição com um header interno e, opcionalmente, reescrever para uma rota que fará fetch do draft do CMS.
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const preview = request.cookies.get('preview_mode');
const url = request.nextUrl.clone();
if (preview) {
// Marca a req para os handlers a seguir saberem que é preview
const res = NextResponse.next();
res.headers.set('x-preview-mode', '1');
return res;
}
// Sem preview: comportamento padrão
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)']
};
O que acontece depois: qualquer rota do App Router pode checar o header x-preview-mode (ou ler cookies) para decidir buscar draft ao invés de conteúdo publicado.
3) Pegar conteúdo com ISR e suportar preview
Na página, use fetch com a opção next: { revalidate: N }. Para preview, force fetch direto do CMS (bypass do cache) e entregue o resultado. Exemplo de componente servidor que busca conteúdo, detecta preview e escolhe a fonte.
// app/posts/[slug]/page.tsx
import { cookies } from 'next/headers';
export default async function PostPage({ params }: { params: { slug: string } }) {
const slug = params.slug;
const isPreview = !!cookies().get('preview_mode');
// O que faz: busca do CMS com ISR para público e pull direto para preview
const url = `${process.env.CMS_URL}/posts/${slug}`;
const res = await fetch(url, {
// Revalida a cada 60s para público
next: { revalidate: 60 }
});
let data = await res.json();
if (isPreview) {
// Re-fetch for preview to ensure draft content, não usar cache
const r2 = await fetch(url + '?draft=true', { cache: 'no-store' });
data = await r2.json();
}
return (
{data.title}
);
}O que acontece depois: visitantes normais recebem versão ISR cacheada; editores com cookie recebem o draft instantaneamente.
4) Server Action para salvar rascunho e revalidar publicacao
Server Actions são ótimas para operações server-only: gravar rascunho no CMS e acionar revalidação das páginas públicas. No processo de publicação, chamamos revalidatePath para forçar ISR a recarregar a versão pública.
// app/(editor)/editor-form.tsx
'use server';
import { revalidatePath } from 'next/cache';
export async function publishPostAction(data: FormData) {
const slug = data.get('slug')?.toString();
const body = {
title: data.get('title'),
content: data.get('content'),
status: 'published'
} as any;
// O que faz: solicita ao CMS a publicação
await fetch(`${process.env.CMS_URL}/posts/${slug}/publish`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
// este fetch roda no servidor, não precisa de next.revalidate
});
// O que acontece: revalida a rota pública para servir conteúdo atualizado
revalidatePath(`/posts/${slug}`);
}
Observação: revalidatePath roda no servidor e agenda a revalidação do cache da rota indicada. Use com parcimônia para não disparar muitas revalidações simultâneas.
Patterns e boas praticas
Autenticação e segurança
Nunca confie somente em um cookie sem validação adequada. O padrão mais seguro é emitir um token de preview assinado no backend (JWT com claims mínimas) e validá-lo no endpoint de preview antes de setar o cookie. Use HttpOnly, SameSite e expiry curto. Evite expor tokens sensíveis ao client JS.
Separação de leitura de conteúdo
Implemente duas rotas de conteúdo no CMS: uma pública (para produção) e outra para drafts. No fetch, use query param ?draft=true ou header específico quando em preview. Isso facilita caching e limites de read scopes no CMS.
Boas práticas adicionais:
Cookie HttpOnly para preview; se precisa acessar client-side, use um cookie secundário não-sensível.
Centralize a validação do token de preview em um serviço backend — o middleware só atua como encaminhador.
Use revalidateTag/revalidatePath para granularidade fina; evite revalidar toda a aplicação.
Fluxo de deploy e cache
Tenha cuidado com deploys que limpam caches; garanta que a revalidação seja compatível com seu CDN. Quando publicar, dispare webhooks para revalidar rotas márgenes (por publicação em lote) em vez de revalidar cada item individualmente.
Armadilhas comuns e como evitar
1) Preview vazando para usuários finais
Erro comum: cookie de preview não ser HttpOnly ou ter escopo muito amplo, fazendo com que links compartilhados contenham token. Evite colocar token em querystrings e use cookies com path fixo e menor tempo de vida.
2) Revalidação excessiva e custos altos
Acionar revalidações em massa ao publicar muitos itens pode sobrecarregar CDN / origin e aumentar custos. Estratégia: agrupe revalidações, use tags e debounce de revalidação para coleções grandes.
Exemplo prático de debounce simples antes de revalidar (pseudo):
// server utility: revalidate-queue.ts (pseudo)
const queue = new Set();
let timeout: NodeJS.Timeout | null = null;
export function scheduleRevalidate(path: string) {
queue.add(path);
if (!timeout) {
timeout = setTimeout(() => {
for (const p of queue) {
// chama revalidatePath(p)
}
queue.clear();
timeout = null;
}, 2000);
}
}
O que acontece depois: revalidações são agrupadas em um lote curto, reduzindo chamadas redundantes.
3) Server Actions sem efeito no cliente (confusão sobre cookies)
Server Actions rodam no servidor e não podem diretamente modificar cookies do navegador resultantes de uma ação invocada por fetch no servidor. Para ativar preview via fluxo cliente -> server action -> set cookie, prefira expor uma rota API que redirecione para o cliente com cookie setado, ou faça o client fetch direto para a rota que setará o cookie. Documente claramente esse comportamento para a equipe.
Conclusao prática
Preview em Next.js App Router é resolvível com uma combinação de rotas API que setam cookies seguros, middleware que marca requisições de preview e páginas que escolhem entre ISR e fetch no runtime conforme o modo. Use Server Actions para operações server-only (salvar, publicar, revalidar), mas não confunda sua capacidade de manipular cookies do cliente — para isso, prefira redirecionamentos ou rotas que retornem Set-Cookie diretamente.
Checklist final para implementação segura e eficiente:
Emitir token de preview assinado no backend.
Endpoint que valida token e seta cookie HttpOnly.
Middleware que detecta cookie e sinaliza preview.
Páginas que buscam draft com
cache: 'no-store'quando em preview.Usar
next: { revalidate }erevalidatePathpara ISR controlado.
Implementando esse padrão você mantém um fluxo de edição seguro para editores, preserva o desempenho com ISR para o público e reduz riscos de vazamento de conteúdo não publicado.
Se for adotar este padrão em produção, valide o fluxo de segurança com auditoria de cookies e monitore as taxas de revalidação para ajustar o debounce/agrupamento conforme a carga real.
Assuntos: Next.js, app-router, isr, preview, middleware, server-actions
Leia também
SSR multi-região no Next.js App Router: sincronizar ISR e invalidar caches
Guia prático para sincronizar ISR em implantações multi-região com Next.js App Router: padrões, Server Actions, endpoint de revalidate, broadcast entre…
Uploads grandes no Next.js App Router: Server Actions + upload resumível para S3
Fluxo prático para uploads grandes com Next.js App Router: use Server Actions para criar multipart uploads e gerar presigned URLs, e implemente no cliente…
WebAuthn sem senha no Next.js (App Router) + Supabase: passo a passo
Guia prático e passo a passo para implementar WebAuthn sem senha em Next.js (App Router) com Supabase: conceitos, endpoints server, componente cliente e dicas…
ISR on-demand com Server Actions: revalidação via webhook e middleware
Como migrar de endpoints de revalidate inseguros para uma arquitetura segura com Server Actions, webhooks assinados e middleware em Next.js App Router…
A/B testing no Next.js (App Router): middleware, cookies e ISR passo a passo
Guia prático para implementar A/B testing no Next.js App Router: use middleware para atribuir variantes, cookies para persistência e rewriting para habilitar…
Formulários sem JavaScript com Server Actions no Next.js (App Router)
Aprenda a criar formulários totalmente funcionais sem JavaScript usando Server Actions no Next.js (App Router). Exemplo prático com código, validação…