Deploy no edge com URLs limpas e dois domínios
Um Worker sem código, servindo arquivos estáticos do edge da Cloudflare. O detalhe que dá trabalho não é o deploy: é fazer `/sobre` responder sem `.html` e sem barra final, e o segundo domínio redirecionar sem custar invocação.

Performance Baseline
- Requisições externas em runtime
- 0
- Tempo de build
- 30s
A parte fácil do deploy num site estático é o deploy. A parte que dá trabalho é
o roteamento: fazer /sobre responder 200 sem extensão, /sobre/ e
/sobre.html redirecionarem para a forma canônica, e rota inexistente cair num
404 de verdade — com status 404, não com 200 e uma página bonita.
Um Worker que não executa nada
O Cloudflare Workers serve arquivos estáticos direto do edge quando você declara
assets sem um main:
{
"name": "rafazingano",
"compatibility_date": "2026-08-03",
"assets": {
"directory": "./dist",
"html_handling": "drop-trailing-slash",
"not_found_handling": "404-page"
},
"routes": [
{ "pattern": "rafazingano.com.br", "custom_domain": true }
]
}Sem main, nenhum código roda em runtime. Não há cold start, não há invocação
faturada, não há superfície de ataque. É CDN com deploy versionado.
O par de configurações que entrega a URL limpa
Isto precisa casar dos dois lados, e é onde eu vi mais gente errar.
No Astro, build.format: 'directory' faz cada página virar
dist/sobre/index.html, e trailingSlash: 'never' faz os links internos
apontarem para /sobre sem barra:
export default defineConfig({
site: 'https://rafazingano.com.br',
output: 'static',
trailingSlash: 'never',
build: { format: 'directory' },
});Do lado do Cloudflare, html_handling: "drop-trailing-slash" é o espelho disso.
O resultado, verificado com wrangler dev antes de subir:
| Requisição | Resposta |
|---|---|
/sobre |
200 |
/sobre/ |
307 → /sobre |
/sobre.html |
307 → /sobre |
/nao-existe |
404 com a página de erro |
Se você usar auto-trailing-slash no Cloudflare com trailingSlash: 'never' no
Astro, cria um par que discorda: os links apontam para uma forma e o servidor
canoniza para outra. Funciona, mas gera um redirecionamento extra em toda
navegação — e o Google indexa as duas versões.
Dois domínios, um canônico
Tenho rafazingano.com.br e rafaelzingano.com.br. Servir o mesmo conteúdo nos
dois divide o ranqueamento entre eles e é conteúdo duplicado aos olhos do Google.
O canônico é o primeiro — é o que consta no meu LinkedIn e no currículo, e casa
com o handle @rafazingano que uso em todo lugar. O outro redireciona com 301
preservando o caminho.
A tentação é resolver isso no Worker. Não faça. Redirecionar dentro do Worker
exige run_worker_first: true, o que passa a invocar código em toda
requisição — inclusive as do domínio certo, que hoje são servidas do edge sem
executar nada. Você trocaria latência e requisições faturadas por conveniência.
O lugar certo é uma Redirect Rule, que roda na borda antes do Worker e não custa invocação:
Quando: Hostname equals rafaelzingano.com.br
Então: URL redirect → Dynamic
concat("https://rafazingano.com.br", http.request.uri.path)
Status 301 · Preserve query string
O DNS da zona que só redireciona aponta AAAA para 100:: — o prefixo de
descarte do IPv6 — com o proxy ligado. Nada roda ali; o registro existe só para
o tráfego chegar à borda da Cloudflare e a regra poder atuar.
O teste que eu rodo antes de todo deploy
Build passando não garante que o roteamento funciona, porque o astro preview
não implementa o html_handling do Cloudflare. O wrangler dev implementa:
pnpm build
npx wrangler dev --port 8788
# canônicas respondem 200
curl -so /dev/null -w '%{http_code}\n' localhost:8788/sobre
# variantes redirecionam
curl -so /dev/null -w '%{http_code} %{redirect_url}\n' localhost:8788/sobre/
curl -so /dev/null -w '%{http_code} %{redirect_url}\n' localhost:8788/sobre.html
# nada de externo escapou para o build
grep -rE 'googleapis|gstatic|cdn\.tailwindcss' dist/ && echo FALHOUAquele último grep é a regra número um do projeto virada teste. Ele já pegou
uma importação de fonte que eu tinha deixado escapar.
Onde as contas fecharam
Voltando às metas que escrevi antes de começar:
| Meta | Resultado |
|---|---|
| Zero JavaScript de framework | 0 KB — só 2,8 KB inline próprios |
| Zero requisição externa | confirmado por grep no build |
| Build abaixo de 30s | 30s cravado, incluindo o índice do Pagefind |
| Publicar = um arquivo | um .mdx e pnpm build |
O build empatou com o limite que eu tinha estipulado. Boa parte do tempo é a otimização de imagem e a geração dos cards sociais — duas coisas que eu não tinha previsto quando estimei.
O que ficou para depois
Nenhum projeto está pronto, e este tem uma lista honesta de pendências:
- Tema escuro. A estrutura de tokens está pronta; falta desenhar a paleta.
- Formulário de contato abre o WhatsApp com a mensagem montada. Funciona bem e não custa nada, mas não deixa registro do que foi enviado.
- Fotos ainda são genéricas.
- A interface de busca está em versão mínima — falta filtro por coleção e destaque do trecho encontrado.
Este diário continua conforme eu resolver cada uma.