Diário do Projeto
rafazingano.com.br

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.

Página de contato do site, com formulário à esquerda e um bloco de código em tema escuro à direita mostrando os dados de contato.

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:

wrangler.jsonc
{
  "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:

astro.config.mjs
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:

Verificação local do runtime real
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 FALHOU

Aquele ú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.

CloudflareWorkersDeployDNS