03/05/2026 #astro #seo #cloudflare
Trailing slash no Astro com Cloudflare Pages: always, never e por que vou migrar pro never+file
Cloudflare Pages força um 308 que quebra trailingSlash never no Astro. Tem duas saídas válidas. Saí do never, fui pro always pra resolver o 308, e agora vou de novo pro never+file por gosto e simplicidade. O caminho contado por inteiro.
Comecei esse site com trailingSlash: 'never', achando que era a escolha mais moderna (URLs limpas, tipo /blog/post, sem barra no fim). Funcionava bem em dev. Em produção no Cloudflare Pages, o Screaming Frog começou a apontar uma cadeia de redirecionamentos 308 que eu não tinha pedido pra ninguém, e o Search Console reclamou de “Page indexing affected”.
A primeira reação foi voltar pra always: barra em todo lugar, configuração documentada como “default seguro pra Cloudflare”, problema do 308 resolvido. Funcionou.
Depois, conversando sobre o post, caiu a ficha: existe uma terceira combinação (never + file) que também resolve o 308 e mantém a URL sem barra, sem deixar .html aparecendo em lugar nenhum. Esse era exatamente o cenário que eu queria desde o começo, só não tinha conectado os pontos.
Então esse post conta os dois trechos: por que never + directory quebra no Cloudflare, por que always é uma saída sólida, e por que estou migrando pra never + file agora — não por ser “tecnicamente melhor”, mas por preferência estética e por evitar a categoria de erro bobo de indexação que o trailing slash gera.
O que é trailing slash, rapidamente
Trailing slash é a barra no fim da URL. marciotoledo.com/blog/ tem, marciotoledo.com/blog não tem. Pra Google, na raiz do domínio dá no mesmo (marciotoledo.com = marciotoledo.com/), mas em qualquer outra rota são URLs distintas. Se as duas respondem 200, vira conteúdo duplicado.
A convenção antiga (Apache, WordPress, sites estáticos antigos) era usar barra no fim porque a URL representava uma pasta no servidor, e o servidor entregava o index.html daquela pasta. /blog/ = /blog/index.html. Era o mapa direto entre URL e sistema de arquivos.
Sites modernos (Next, Astro, frameworks SPA) tendem a abandonar a barra porque a URL não representa mais pasta nenhuma, é só uma rota lógica. Daí a tentação do never.
Onde o Cloudflare Pages estraga a festa
O Cloudflare Pages tem um comportamento documentado e não desligável: quando alguém acessa /blog, ele procura /blog.html. Se não acha mas encontra /blog/index.html, ele redireciona com 308 permanente pra /blog/.
A documentação do Astro reconhece isso de forma direta: “Trailing slashes on prerendered pages are handled by the hosting platform, and may not respect your chosen configuration.” Ou seja: você pode setar trailingSlash: 'never' no astro.config.mjs, que o Cloudflare ignora e redireciona mesmo assim.
Resultado prático no meu caso: tinha links internos sem barra (/works, /blog, /links/), o Cloudflare redirecionava cada um deles com 308 pra versão com barra, e o Screaming Frog apontava isso como redirect chain ou non-indexable URL. Não é fim do mundo, mas é desperdício de crawl budget e gera ruído nos relatórios de SEO.
As três opções no Astro
O Astro tem trailingSlash: 'always' | 'never' | 'ignore', e o build.format pode ser 'directory' (gera /blog/index.html) ou 'file' (gera /blog.html). A combinação dos dois define o que sai do build.
{% table %}
- Config
- URL final no browser
- Como você escreve o link
- Estrutura no disco
- Comporta no Cloudflare Pages?
trailingSlash: 'always'+format: 'directory'(default)/blog//blog//blog/index.html- Sim, caminho natural do Cloudflare. Sem redirect.
trailingSlash: 'never'+format: 'directory'- redireciona pra
/blog/ /blog/blog/index.html- Não. Cloudflare força 308. Quebra.
trailingSlash: 'never'+format: 'file'/blog/blog/blog.html- Sim, Cloudflare acha o
.htmldireto. Sem redirect. {% /table %}
Coluna importante: “Como você escreve o link”. No never + file, você escreve <a href="/blog/post">, sem barra e sem .html. O .html é detalhe interno do build, nunca aparece na URL pública nem no link que o leitor copia. Foi essa parte que me confundiu inicialmente, achando que format: 'file' deixaria .html exposto.
Por que primeiro fui pro always
Quando vi os 308 do Cloudflare apontados no Screaming Frog, a saída mais rápida foi inverter de never pra always: tudo passa a ter barra, Cloudflare entrega direto, problema resolvido. Dois commits, sitemap regenerado, links internos do Layout.astro ajustados pra garantir o / final.
Funcionou. O Search Console parou de reclamar, o Screaming Frog ficou limpo. Mas a barra em todo lugar nunca me agradou esteticamente.
Por que vou migrar pro never + file
A escolha pelo never + file daqui pra frente é preferência minha, não recomendação técnica universal:
- Estética: prefiro
/blog/postsobre/blog/post/. URL sem barra é o que Vercel, Netlify e Next.js entregam por default. O resto do mundo moderno caminhou pra esse lado. - Simplicidade visual: menos caracteres, mais limpo de copiar e colar, mais fácil de ler em listagens.
- Evitar a categoria do erro: com
always, você sempre depende de todos os links internos terminarem com/pra não cair em 308. Um link esquecido sem barra (em post, em footer, em CTA) gera redirect. Comnever + file, esse modo de falhar simplesmente não existe.
Não é que never + file seja tecnicamente superior a always + directory. Em SEO, o Google trata as duas como equivalentes. É preferência estética + redução de uma classe de erro chata.
Plano de migração
Pra fechar o post de forma honesta, esse é o plano que vou executar (provavelmente num post de follow-up):
astro.config.mjs:trailingSlash: 'never'e adicionarbuild: { format: 'file' }.- Sitemap: a função
getBlogPages()monta URLs com/no fim. Tirar. Layout.astro: remover a lógica que adiciona/em paths internos.- Links internos: header, footer, nav, links em posts (.md) — tirar a barra final.
- Middleware (
src/middleware.ts): redirects/en/links→/links/viram/en/links→/links. _redirectsnopublic/: mapear/blog/post/→/blog/post301 pra preservar link juice durante o período em que o Google ainda tem as versões com barra indexadas.
Resumo
No Cloudflare Pages, evite trailingSlash: 'never' com o format: 'directory' default. Essa combinação é a que quebra: o Cloudflare força 308, o Search Console reclama, o Screaming Frog acende alerta. Não é bug do Astro, é design do Cloudflare, e não tem flag pra desligar.
Sobram duas saídas igualmente válidas:
always + directory→ URLs com barra (/blog/). Default seguro, zero refactor se você já estava aí.never + file→ URLs sem barra (/blog). Estética moderna, elimina a classe de erro do “esqueci a barra no link”.
Entre as duas, é gosto e contexto. Não tem ganho de SEO de um sobre o outro. Google trata as duas como válidas desde que sejam consistentes.
Pra Vercel ou Netlify, never funciona normalmente com o default directory — as duas plataformas respeitam a config do Astro sem reescrever rota.
A recomendação geral de SEO segue válida: escolha um padrão e seja consistente. Google não premia nem pune nenhum dos dois, mas pune inconsistência (mesma página acessível com e sem barra, sem canonical).
Agradecimento
Quem me destravou nesse problema foi o Morris Liu, num artigo curto e direto explicando exatamente o porquê do trailingSlash não funcionar como esperado no Cloudflare Pages e a saída via build.format: 'file'. Sem aquele post, eu provavelmente teria ficado preso no always por mais tempo achando que era a única opção. Vale a leitura.
Referências
- Astro Documentation. Configuration Reference: trailingSlash
- Cloudflare Pages Docs. Redirects
- Cloudflare Community. Disable Non-Trailing Slash 308 Redirect (feature request)
- Morris Liu. Fixing Astro SEO on Cloudflare Pages: Why trailingSlash Doesn’t Work (and What Does)
- GitHub. Cloudflare adapter strips trailing slash, redirect becomes 404 (#13165)
- GitHub. v5.5.2 trailingSlash setting does not work (#13423)
- Zach Leatherman. Trailing Slashes on URLs: Contentious or Settled?
- Ahrefs. What Is A Trailing Slash & When Does It Matter
- Aurelien Kinet. How to remove trailing slash in URLs with Astro and Cloudflare
- Google Search Central. Does trailing slashes affect SEO?