SEO & Security.txt
Todo projeto Kyrux traz por padrão o app embutido apps/meta, instalado
automaticamente (já listado em core/apps/installed.go) — sem precisar
rodar startapp nem registrar nada manualmente. Ele serve três arquivos
que praticamente todo site público precisa e que, sem isso, cada projeto
acaba reimplementando do zero:
| Rota | O que é |
|---|---|
GET /robots.txt |
Libera indexação total (Allow: /) e aponta pro sitemap |
GET /sitemap.xml |
Lista de páginas indexáveis |
GET /.well-known/security.txt |
Contato de segurança, formato RFC 9116 |
GET /security.txt |
Redireciona (301) para /.well-known/security.txt — alguns scanners antigos ainda checam a raiz primeiro |
Separação: routes.go só mapeia caminho, views.go tem o conteúdo
apps/meta/
├── routes.go ← router.Path("GET", "/robots.txt", views.RobotsView(fw), ...)
└── views/
└── views.go ← conteúdo, montagem do XML/texto, fallback sem APP_URL
Essa separação não é cosmética: routes.go fica legível como um índice de
URLs do projeto — pra saber o que uma rota faz, você olha a view, não
precisa ler lógica de string-building no meio da lista de rotas.
APP_URL — a origem canônica
# .env
APP_URL=https://meusite.com.br
APP_URL (sem barra final) é usada pra montar as URLs absolutas de
Sitemap: no robots.txt, <loc> no sitemap.xml e Canonical: no
security.txt. Sem ela configurada, os três arquivos continuam
funcionando — só omitem os campos que dependeriam do domínio (sitemap.xml
sai com <loc> relativo, robots.txt sem a linha Sitemap:, security.txt
sem Canonical:). Configure antes de ir pra produção.
Customizando o conteúdo
Os três handlers ficam em apps/meta/views/views.go — edite direto ali:
// sitemapPaths lista as páginas públicas do projeto. O framework não
// descobre isso sozinho a partir das rotas registradas — nem toda rota
// GET é uma página que faz sentido indexar (endpoints JSON, rotas com
// parâmetro, etc).
var sitemapPaths = []string{
"/",
"/sobre/",
"/contato/",
}
// securityContact e securityExpires — troque pelo contato real de
// segurança do projeto e revise Expires todo ano (RFC 9116: depois dessa
// data, scanners voltam a reportar o arquivo como ausente/inválido mesmo
// ele existindo).
const (
securityContact = "mailto:[email protected]"
securityExpires = "2027-01-01T00:00:00.000Z"
)
Se o projeto já tem uma fonte de verdade pras próprias páginas — um slice de
seções, resultado de uma query, o retorno de Router.Routes() filtrado —
troque o slice fixo sitemapPaths por uma função que monte a lista a partir
dela, em vez de manter os dois sincronizados manualmente. É exatamente o que
este site faz: seu sitemap.xml é montado a partir de content.All(), a
mesma fonte que alimenta a navegação da documentação — cada seção nova em
apps/docs/content/markdown/ aparece no sitemap sem editar views.go.
robots.txt bloqueando alguma área
Por padrão o RobotsView libera tudo (Allow: /). Pra restringir uma área
(ex: /admin/), edite o corpo montado em RobotsView — não tem flag de
configuração pra isso, é só string.