Destino Dev

Blog

A/B testing no Next.js (App Router): middleware, cookies e ISR passo a passo

Por John Pires · 29 de março de 2026

A/B testing no Next.js (App Router): middleware, cookies e ISR passo a passo

1) O que é e por que importa

A/B testing é a prática de mostrar variantes diferentes de uma experiência (A e B) para subconjuntos de usuários e medir qual versão performa melhor. Para equipes que usam Next.js com App Router, o desafio prático é: como garantir que cada usuário veja consistentemente a mesma variante, que as variantes sejam cacheáveis (ISR) e que não quebrem a performance da aplicação?

Problema comum: muitas soluções simples trocam o conteúdo apenas no cliente via JavaScript depois que a página carrega. Isso quebra vantagens do SSR/ISR (tempo até conteúdo visual, SEO e cache). O objetivo aqui é uma solução que:

  • Assigne variante via middleware na borda (edge).

  • Persista a variante em cookie para consistência entre visitas.

  • Use rewriting para criar rotas cacheáveis separadas por variante, permitindo ISR sem contaminá-lo.

Este artigo guia do zero, com analogias e código real em App Router, mostrando porque a técnica funciona e quais são as armadilhas comuns.

Analogia curta para fixar

Pense em um teatro: o bilheteiro (middleware) decide se você entra na sessão A ou B, carimba seu ingresso (cookie) e te encaminha para a porta correta (rewrite). O espetáculo (page) já está configurado e pode ser pré-composto (ISR) por sessão. Assim, quem entrou na sessão A sempre verá A, e o cache do teatro pode ser separado por sala.

Quando isso importa

Se usa medição de conversão, testes de layout, ou experimentos de conteúdo que impactam SEO e carregamento inicial, precisa que o servidor entregue a variante correta já na primeira resposta. Além disso, ao aproveitar ISR você reduz custo e melhora latência — se separar por variante.

2) Conceitos fundamentais com analogias

Middleware (o bilheteiro)

Middleware em Next.js roda antes de chegar ao roteamento final. É executado na borda com NextRequest/NextResponse. Aqui ele é perfeito para decidir e persistir a variante com um cookie e reescrever a URL para uma rota de variante específica. Isso garante que o pipeline de renderização veja a variante como parte da rota.

Analogia: o middleware é o bilheteiro que olha sua identidade e decide para qual sala você vai. Ele também escreve o carimbo para futuras entradas.

Cookies (o carimbo)

Um cookie persiste a decisão entre visitas. No App Router, você pode ler cookies do servidor com cookies() de next/headers em componentes/rotas, e no middleware com req.cookies. Um cookie simples {variant: 'A'|'B'} é suficiente para consistência.

Importante: configure o cookie com atributos corretos (path, max-age, sameSite, secure) para evitar problemas de privacidade ou falta de persistência.

ISR e cache por variante (as salas separadas)

ISR (Incremental Static Regeneration) permite páginas estáticas regeneradas em background. Para manter ISR e ainda servir variantes distintas, cada variante precisa existir em uma rota separada no nível do CDN/Edge. Se você só trocar conteúdo com JavaScript, perde o benefício do ISR. A técnica: middleware rewrites a URL como /_ab/[variant]/original-path — Next.js trata isso como outra rota estática/ISR.

Resumo técnico: reescrever a rota é a chave para combinar persistência de variante via cookie com ISR.

3) Passo a passo com código comentado

Aqui vamos construir a pipeline: middleware que decide a variante e reescreve, páginas em app/ab/[variant]/... que têm export const revalidate = 60, e leitura da variante no componente server para renderizar a versão correta. Em seguida, um pequeno server action para registrar exposição (opcional).

Escolha da estratégia de alocação

Estratégias comuns de alocação: aleatória 50/50, seedada por IP, ou baseada em cookie já existente. Comece com 50/50 aleatório e persista em cookie. Para produção, use um serviço de experimentação para registrar exposição e garantir distribuição precisa.

Middleware: atribuir variante e rewrite

Este middleware decide a variante, setta o cookie e reescreve o path para incluir a variante. Coloque em middleware.ts na raiz do projeto. Contexto: roda na borda para cada request antes do roteamento.

Código (o que faz): decide entre 'a' e 'b', seta cookie com 30 dias, reescreve para /ab/[variant]/[...path] mantendo query.

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export const config = {
  matcher: '/:path*', // aplica em todo o site; filtre se necessário
};

export function middleware(req: NextRequest) {
  const url = req.nextUrl.clone();
  const cookieVariant = req.cookies.get('variant')?.value;

  // Se já existe cookie, usamos ele; caso contrário, escolhemos aleatoriamente
  const variant = cookieVariant ?? (Math.random() < 0.5 ? 'a' : 'b');

  // Se cookie não estava presente, setamos
  const res = NextResponse.next();
  if (!cookieVariant) {
    // 30 dias em segundos
    res.cookies.set('variant', variant, { maxAge: 60 * 60 * 24 * 30, path: '/' });
  }

  // Reescreve para rota que contém a variante. Isso permite cache/ISR por variante.
  // Mantemos a URL visível; apenas reescrevemos internamente.
  url.pathname = `/ab/${variant}${url.pathname}`;
  return NextResponse.rewrite(url);
}

O que acontece: após o middleware rodar, o request vai internamente para /ab/a/... ou /ab/b/..., portanto as rotas /ab/[variant]/... recebem requests distintos por variante e podem ser geradas com ISR.

Página App Router com ISR por variante

Agora criamos uma página em app/ab/[variant]/page.tsx que será gerada estaticamente e revaliada a cada 60 segundos. Contexto: Next.js tratará /ab/a/... e /ab/b/... como rotas diferentes, com cache próprio.

// app/ab/[variant]/page.tsx
import { cookies } from 'next/headers';
import type { Metadata } from 'next';

export const revalidate = 60; // ISR: revalida a cada 60 segundos

interface Props {
  params: { variant: string };
}

export default function ExperimentPage({ params }: Props) {
  // Reading cookie is optional here; params.variant já informa a variante
  const cookieStore = cookies();
  const cookie = cookieStore.get('variant')?.value;
  const variant = params.variant;

  return (
    
      
        
          Versão {variant.toUpperCase()}Cookie detectado: {cookie ?? 'nenhum'}
            {variant === 'a' ? (Experimento A — Cabeçalho em destaque Conteúdo voltado para a variação A.
            ) : (Experimento B — Layout alternativo Conteúdo voltado para a variação B.
            )}
               
    
  );
}


O que acontece: Next.js gera páginas separadas para /ab/a/* e /ab/b/* e aplica ISR com o intervalo configurado. Usuários recebem a página já renderizada com a variante correta.

Server Action opcional: registrar exposição

Registrar o evento de exposição (impression) em um endpoint é prático para análise. No App Router você pode usar Server Actions (arquivo em server actions) ou criar uma rota API. Aqui um exemplo de Server Action simples que grava em um backend fictício. Contexto: chamada do componente servidor ao montar para registrar que o usuário viu a variante.

// app/actions/registerExposure.ts
'use server';

export async function registerExposure(variant: string, path: string) {
  // Simulação de chamada a um endpoint externo.
  await fetch('https://meu-analytics.example.com/exposure', {
    method: 'POST',
    body: JSON.stringify({ variant, path, timestamp: Date.now() }),
    headers: { 'Content-Type': 'application/json' },
  });
}

O que acontece: quando invocado de um componente servidor, registra a exposição no backend. Em produção, adicione filas/resiliência e deduplicação para não inflar números.

4) Exemplo prático completo

Aqui juntamos tudo: estrutura de pastas, middleware, página de variante e uma rota estática de exemplo. Vou listar arquivos essenciais e depois explicar como testar localmente.

Estrutura mínima

  1. middleware.ts

  2. app/ab/[variant]/page.tsx

  3. app/page.tsx (roteia normalmente para raiz e será reescrita pelo middleware)

  4. app/actions/registerExposure.ts (opcional)

Passo a passo para testar localmente:

  1. Crie um projeto Next.js (v13.4+ com App Router): npx create-next-app@latest --experimental-app.

  2. Adicione o middleware.ts no root com o código mostrado.

  3. Crie app/ab/[variant]/page.tsx com export const revalidate = 60.

  4. Inicie com npm run dev e abra a raiz /. O middleware deve reescrever internamente para /ab/a/ ou /ab/b/.

  5. Verifique no navegador: um cookie chamado variant estará presente e o conteúdo deve corresponder à variante exibida.

Comportamento esperado

Na primeira visita sem cookie, você é atribuído aleatoriamente e recebe um cookie. Em visitas subsequentes, o cookie persiste a variante. Cada variante tem sua cache/ISR própria porque middleware reescreve a rota para um prefixo que inclui a variante.

Se quiser ver a outra variante, remova o cookie no DevTools e recarregue — a atribuição ocorrerá novamente.

Erros comuns e armadilhas

Veja a seguir problemas que aparecem com frequência e como evitá-los.

1. Cookie não está sendo setado ou lido

Possíveis causas:

  • Rodando em ambiente que bloqueia cookies (modo incognito com bloqueio, políticas de SameSite). Configure sameSite e secure adequadamente para o domínio.

  • No middleware, foi retornado NextResponse.redirect sem preservar cookies; use NextResponse.next() e res.cookies.set antes do redirect/rewrite.

2. ISR não funciona porque a rota não é diferenciada

Se você só altera o conteúdo no cliente com JavaScript, o conteúdo servido pelo CDN é o mesmo para todos. A solução é reescrever para rotas diferentes por variante, como /ab/a/ e /ab/b/, assim cada rota tem seu próprio cache.

3. Testes locais vs produção

O comportamento do middleware e cookies na borda pode variar entre next dev e builds em produção. Sempre verifique no build de produção next build && next start e, idealmente, em um ambiente de preview no Vercel para testar comportamento de edge.

Tip: use logs no middleware (console.log) para debug local, mas remova antes de promover. Em produção, envie eventos para um logger central se necessário.

5) Próximos passos para aprofundar

Medição e deduplicação

Integre um backend de experimentação para registrar exposições e conversões com idempotência. Não dependa apenas de logs do frontend: use Server Actions ou rotas API que registrem eventos do lado do servidor, com retries e deduplicação.

Melhorias na atribuição

Substitua aleatório por estratégias mais avançadas: hashing de user id, controle de taxa, alocação baseada em feature flags ou segmentação por geografia. Serviços como Growthbook, Optimizely ou LaunchDarkly oferecem controle mais robusto.

Considerações de privacidade e conformidade

Defina a validade do cookie e respeite preferências do usuário (consentimento). Cookies de experimento podem ser considerados rastreamento dependendo do local; sempre implemente mecanismos de consentimento quando necessário.

Quando não usar middleware

Se o seu experimento é puramente client-side (por exemplo, troca visual que não impacta SEO) e você não precisa de ISR, a implementação no cliente pode ser suficiente e menos complexa. Avalie trade-offs entre performance/SEO e simplicidade.

Conclusão prática

Combinar middleware, cookies e rewrites é uma abordagem pragmática para A/B testing no Next.js App Router que preserva os benefícios do SSR/ISR. O middleware atribui e persiste a variante; o rewrite cria rotas separadas que permitem cache/ISR por variante; as páginas em app/ab/[variant]/... são geradas com revalidate.

Checklist para implementação rápida:

  • Implementar middleware que atribui variante e seta cookie.

  • Reescrever para prefixo de variante para separar caches.

  • Construir páginas em app/ab/[variant] com export const revalidate.

  • Registrar exposições com Server Actions ou API para análise.

Seguindo esses passos você terá A/B tests que são consistentes entre visitas, amigáveis ao SEO e cacheáveis via ISR — sem sacrificar performance.

Erros e melhorias estão previstos: sempre monitore os resultados, teste em produção e refine a estratégia de atribuição conforme necessário.

Boa prática final: mantenha suas rotas de variante hierarquizadas e documente como limpar cookies em ambiente de teste para facilitar QA.

Assuntos: Next.js, app-router, middleware, ab-testing, isr

Leia também