A/B testing e redirects
withABTesting + padrão SITES_KV; loadRedirects de blocks; padrão CSV-redirects.
Esta página cobre dois temas operacionais: A/B testing (servir variants alternativos) e redirects (mover usuários de URLs antigas para novas). Ambos vivem na camada do Worker, antes do framework ver o request.
A/B testing
Quando usar withABTesting
withABTesting é para A/B no nível de versão de site — comparar codebases inteiros (canary deploys, experimentos full-stack). Para A/B no nível de conteúdo (mudar uma section), use matchers do CMS.
| Cenário | Use |
|---|---|
| Mudar texto/imagem de um Hero | Matcher CMS + multivariate block |
| Testar nova nav inteira contra a velha | withABTesting |
| Migração gradual v1 → v2 com 10% do tráfego | withABTesting |
| Targeting de feature flag (logado vs deslogado) | Matcher CMS |
Setup
// src/worker-entry.ts
import { createDecoWorkerEntry } from "@decocms/start/sdk/workerEntry";
import { withABTesting } from "@decocms/start/sdk/abTesting";
const decoWorker = createDecoWorkerEntry(serverEntry, { admin: { /* ... */ } });
export default withABTesting(decoWorker, {
kvBinding: "SITES_KV",
preHandler: (request, url) => null,
});
KV namespace
withABTesting lê variants do KV. Crie e bind:
npx wrangler kv:namespace create SITES_KV
Adicione no wrangler.jsonc :
{
"kv_namespaces": [
{ "binding": "SITES_KV", "id": "abc123..." }
]
}
Schema de variants
KV holds entries chaveados por host:
www.minha-loja.com → { variants: [{ name: "v1", weight: 90 }, { name: "v2", weight: 10 }] }
Quando um request chega:
withABTestinglê entry do KV.- Determina o variant via cookie + buckets ponderados.
- Posta para o Worker variant target (ou aceita “default” e roda o Worker corrente).
Cookie _deco_variant=<name> faz o variant ser sticky para a sessão.
Padrões de deploy
Canary deploy:
Worker A (production current) — peso 90
Worker B (next version) — peso 10
Bumpa para 50/50 quando Worker B passar. Promove para 100/0 cortando A.
Migração gradual:
Worker v1-fresh (legacy) — peso 80
Worker v2-tanstack (new) — peso 20
Aumenta peso v2 conforme métricas saem boas.
preHandler
Roda antes do A/B routing. Use para shortcut em redirects ou bypass de A/B em paths:
preHandler: (request, url) => {
if (url.pathname.startsWith("/admin")) {
return new Response("Not allowed", { status: 403 });
}
return null;
},
Devolva Response para shortcut, null para continuar para A/B routing.
Tracing variants
Toda variação assigned é loggada. Filtre wrangler tail :
npx wrangler tail --search "_deco_variant=v2"
Adicione _deco_variant aos events do seu coletor de analytics para análise downstream.
Redirects
loadRedirects (driven pelo CMS)
Padrão preferido. Edita redirects no admin como blocks; framework carrega:
// src/worker-entry.ts
import { loadBlocks } from "@decocms/start/cms";
import { loadRedirects, matchRedirect } from "@decocms/start/sdk/redirects";
const cmsRedirects = loadRedirects(loadBlocks());
const proxyHandler = (request: Request, url: URL) => {
const redirect = matchRedirect(url.pathname, cmsRedirects);
if (redirect) {
const target = url.search ? `${redirect.to}${url.search}` : redirect.to;
return new Response(null, {
status: redirect.status,
headers: { Location: target },
});
}
return null;
};
const decoWorker = createDecoWorkerEntry(serverEntry, {
admin: { /* ... */ },
proxyHandler,
});
Block:
{
"__resolveType": "$live/redirects/Redirect.ts",
"from": "/old-product",
"to": "/p/new-product",
"status": 301
}
OK para até ~1000 redirects. Para larger, prefira CSV.
Padrão CSV-redirects
Para 10.000+ redirects (típico de sites pré-migração com URLs legacy), loadRedirects é lento — todo decofile resolve. Use CSV em vez disso.
Um padrão comum é armazenar redirects num CSV:
// src/middleware/redirects.ts
import redirects from "../../data/redirects.csv?raw";
const map = new Map<string, { to: string; status: number }>();
for (const line of redirects.split("\n").slice(1)) {
const [from, to, status] = line.split(",");
if (from && to) map.set(from.trim(), { to: to.trim(), status: Number(status) || 301 });
}
export const matchCsvRedirect = (pathname: string) => map.get(pathname) ?? null;
?raw em Vite inlina o conteúdo do CSV em build time. Sem leitura runtime; lookup O(1).
Conecte no proxyHandler igual ao CMS-driven.
Combo CMS + CSV
CMS para redirects de longa cauda editáveis (poucos, mexidos por content team), CSV para legacy bulk (muitos, raramente tocados):
const proxyHandler = (request: Request, url: URL) => {
const cms = matchRedirect(url.pathname, cmsRedirects);
if (cms) return redirectResponse(cms);
const csv = matchCsvRedirect(url.pathname);
if (csv) return redirectResponse(csv);
return null;
};
CMS ganha porque é o que o time edita ativamente.
Cache de redirects
Redirects são respostas Cache-Control = bypass . Cloudflare ainda cacheia o redirect em si (HTTP 301 são cacheable por default), então depois do primeiro hit é instantâneo.
Para invalidar quando você atualizar o mapa de redirects, faça deploy — bundle redeploy gera novo Worker e reset cache.
301 vs 302
301= permanente. Browsers cache forever. SEO transfere link equity.302= temporário. Sem cache; re-checa em todo hit.
Use 301 para URLs legacy (preferido). Use 302 para redirects de campanha ( /black-friday-2024 → /c/promocao durante campanha; depois apaga).
Veja também
Found an error or want to improve this page?
Edit this page