03/05/2026

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.

Trailing slash no Astro com Cloudflare Pages: always, never e por que vou migrar pro never+file

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.

ConfigURL final no browserComo você escreve o linkEstrutura no discoComporta no Cloudflare Pages?
trailingSlash: 'always' + format: 'directory' (default)/blog//blog//blog/index.htmlSim, caminho natural do Cloudflare. Sem redirect.
trailingSlash: 'never' + format: 'directory'redireciona pra /blog//blog/blog/index.htmlNão. Cloudflare força 308. Quebra.
trailingSlash: 'never' + format: 'file'/blog/blog/blog.htmlSim, Cloudflare acha o .html direto. Sem redirect.

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:

  1. Estética: prefiro /blog/post sobre /blog/post/. URL sem barra é o que Vercel, Netlify e Next.js entregam por default. O resto do mundo moderno caminhou pra esse lado.
  2. Simplicidade visual: menos caracteres, mais limpo de copiar e colar, mais fácil de ler em listagens.
  3. 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. Com never + 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):

  1. astro.config.mjs: trailingSlash: 'never' e adicionar build: { format: 'file' }.
  2. Sitemap: a função getBlogPages() monta URLs com / no fim. Tirar.
  3. Layout.astro: remover a lógica que adiciona / em paths internos.
  4. Links internos: header, footer, nav, links em posts (.md) — tirar a barra final.
  5. Middleware (src/middleware.ts): redirects /en/links/links/ viram /en/links/links.
  6. _redirects no public/: mapear /blog/post//blog/post 301 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 #seo #cloudflare

Tags

Se eu te ajudei de alguma forma e você quiser retribuir, pode me pagar um café ☕ ou ainda usar um dos meus links de indicação para abrir conta em serviços como Asaas, Bunny, Clara, assinar conteúdos gratuitos ou usar serviços recomendados.

Você pode me encontrar no: Github, Behance, LinkedIn, YouTube, Instagram, X ou por email hello@marciotoledo.com!

© 2002 - 2026 | Marcio Toledo. Todos os direitos reservados. For LLMs