
# Robots d'indexation, sitemaps et flux

Un moteur de recherche, un lecteur de flux et un assistant d'IA lisent chacun une application au travers d'un petit ensemble de fichiers bien connus. Sovrium les dérive tous de la configuration que vous avez déjà écrite : rien à générer à la main, rien à garder synchronisé.

| Adresse                       | Servi quand                                  | Contenu                                                                                   |
| ----------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `/sitemap.xml`                | Toujours                                     | Un `<url>` par page indexable et par enregistrement public, ou un index au-delà de 5 000  |
| `/robots.txt`                 | Toujours                                     | Une politique d'exploration et l'adresse absolue du sitemap                               |
| `/feed.xml`                   | Une page de collection publique déclare `rss` | Un flux RSS 2.0 des enregistrements les plus récents de cette collection                  |
| `<article>.md`                | Chaque article d'un répertoire de contenu    | Le Markdown de l'article, sans son frontmatter                                            |
| `/llms.txt`, `/llms-full.txt` | L'application a une page de répertoire de contenu | Un index et une concaténation pour les assistants d'IA — voir **Publier llms.txt**   |

## Définir `BASE_URL` d'abord

Chaque URL de ces fichiers est absolue, et un robot fait confiance à l'origine qu'on lui donne. Sovrium prend cette origine dans la variable d'environnement `BASE_URL` ; sans elle, il se rabat sur l'hôte par lequel la requête est arrivée, qui derrière un reverse proxy peut être une adresse que personne ne peut atteindre depuis l'extérieur.

La même variable décide si une page publie des alternatives `hreflang` tout court. Une page dotée d'un `canonical` absolu prend son origine dans cette URL ; sinon, `BASE_URL` la fournit (`SOVRIUM_BASE_URL` pendant `sovrium build`) ; et sans l'un ni l'autre, la page n'émet **aucune** alternative, car les moteurs de recherche rejettent les alternatives relatives. Une application multilingue démarrée sans `BASE_URL` affiche un avertissement au démarrage qui le signale — définissez-la.

## `/sitemap.xml`

Une page est listée lorsqu'elle est lisible anonymement, n'est pas `noindex`, n'est pas exclue par `sitemap: false` et ne se trouve pas sous un chemin commençant par `/_`. Une page de répertoire de contenu se déploie en une entrée par fichier Markdown, et une application multilingue liste chaque page une fois par langue, avec des alternatives `hreflang` et un `x-default` pointant vers la langue par défaut.

```yaml
name: my-site
pages:
  - name: Home
    path: /
    sitemap: { priority: 1.0, changefreq: weekly }
    components:
      - { type: text, element: h1, content: 'Home' }
  - name: Thank you
    path: /thanks
    sitemap: false
    components:
      - { type: text, element: h1, content: 'Thanks for signing up' }
```

**`priority` et `changefreq` sont des indications que Google ignore.** Elles sont conservées parce que d'autres moteurs les lisent encore, mais elles ne changent ni la fréquence à laquelle Google explore une page ni son classement. Ce que Google utilise, c'est `lastmod` — et uniquement pour un site dont il a constaté que les dates sont exactes — si bien que Sovrium n'en écrit un que lorsqu'il le connaît : un article de répertoire de contenu porte la date de modification de son fichier Markdown, sous forme d'horodatage ISO 8601 complet tel que `2026-03-14T09:26:53Z`. Une page déclarée dans la configuration n'a aucune date que le moteur pourrait connaître : son entrée ne porte donc aucun `lastmod` plutôt qu'un faux.

Une page de collection telle que `/blog/:slug` se déploie en une entrée par enregistrement, au slug résolu de l'enregistrement — mais seulement lorsqu'un visiteur anonyme pourrait lire l'enregistrement : sa table doit déclarer `permissions: { read: all }` et aucune règle de lecture au niveau des lignes, le champ à partir duquel l'adresse est construite doit lui aussi être lisible par tous (une entrée `permissions.fields` qui le restreint exclut tous les enregistrements), les enregistrements supprimés sont écartés, et le `collection.filter` propre à la page s'applique. Les enregistrements sont lus par pages de 5 001 lignes, dix pages au plus par collection. Le `lastmod` d'un enregistrement est son champ `updated-at`, lorsque la table en possède un. Une collection portant sur une table qui exige une session ne contribue rien, car un robot recevrait une réponse 404. Un `sovrium build` liste les mêmes enregistrements, lus dans la base de données vers laquelle il pointe — voir plus bas.

**Le sitemap est mis en cache 60 secondes.** Chaque réponse `/sitemap.xml` et `/sitemap-N.xml` est calculée une fois puis servie inchangée pendant une minute : un enregistrement ajouté ou supprimé apparaît donc au plus une minute plus tard ; redémarrer ou recharger l'application repart d'un cache neuf.

Au-delà de 5 000 URL, `/sitemap.xml` devient un `<sitemapindex>` qui nomme `/sitemap-1.xml`, `/sitemap-2.xml` et ainsi de suite par adresse absolue, chacun contenant au plus 5 000 entrées. Le protocole en autorise 50 000 ; le découpage plus bas garde chaque réponse légère.

## `/robots.txt`

```text
User-agent: *
Allow: /
Disallow: /_preview
Sitemap: https://example.com/sitemap.xml
```

Chaque robot est autorisé partout, avec une ligne `Disallow` par page dont le chemin commence par `/_`, suivie de l'adresse absolue du sitemap.

**Une page `noindex` n'est volontairement pas en `Disallow`.** Une page refusée dans `robots.txt` n'est jamais récupérée : un robot ne verrait donc jamais sa balise `noindex` — et une URL qu'il connaît déjà par un lien pourrait rester dans l'index, affichée sans description. Laisser la page explorable est ce qui permet à la balise de prendre effet : marquer une page `noindex` suffit donc à la tenir hors des résultats de recherche.

**Sovrium n'a pas de politique par robot.** Le fichier nomme un seul user agent, `*`, et aucune option ne permet d'en écrire un autre. Cela compte pour les robots d'IA, qui s'identifient par leurs propres jetons et se répartissent en deux catégories : les robots qui récupèrent une page pour répondre à une question en temps réel (`OAI-SearchBot`, `Claude-SearchBot`, `PerplexityBot`) et les robots qui collectent du texte pour entraîner un modèle (`GPTBot`, `ClaudeBot`, `CCBot`, `Bytespider`, plus les jetons `Google-Extended` et `Applebot-Extended`, qui contrôlent l'usage pour l'entraînement sans robot distinct). Tous lisent aujourd'hui `User-agent: *` : une application Sovrium les autorise donc tous. Une demande de politique particulière n'est de toute façon pas respectée par tous les robots : `robots.txt` est une requête, pas un contrôle d'accès.

## `/feed.xml`

Une page de collection qui déclare `rss` publie ses enregistrements les plus récents sous forme de flux RSS 2.0 — `rss: true` pour les vingt éléments par défaut, ou `rss: { limit: 25 }`. La route répond 404 lorsqu'aucune page ne s'y inscrit. La page à partir de laquelle le flux est construit l'annonce dans son en-tête avec `<link rel="alternate" type="application/rss+xml">`, intitulé comme le canal du flux : un lecteur de flux à qui l'on donne l'adresse de la page découvre ainsi le flux de lui-même. Les autres pages n'annoncent rien.

Seule une page que tout le monde peut ouvrir peut publier le flux : une page dont l'`access` exige une connexion ou un rôle ne le construit jamais, même pour un lecteur connecté, car `/feed.xml` est un unique document mis en cache publiquement pour tous les lecteurs. Lorsqu'une page publique déclare aussi `rss`, le flux est construit à partir de la première ; lorsqu'aucune ne le fait, la route répond 404, jamais 403, afin de ne pas révéler l'existence d'un flux restreint.

## Jumeaux Markdown

Chaque article d'un répertoire de contenu est aussi servi en Markdown : ajoutez `.md` à son adresse, ou demandez l'article lui-même avec `Accept: text/markdown`. Le corps est le fichier sans son frontmatter, et un article que le visiteur n'a pas le droit de lire répond 404 sous les deux formes. Le Markdown d'un article public peut être mis en cache par un cache partagé pendant cinq minutes ; le Markdown d'un article dont la page est restreinte par `access` porte la même politique `private, no-cache` que son HTML, de sorte qu'aucun cache partagé ne puisse stocker la copie d'un lecteur et la servir à un autre. Comme une même URL répond deux corps, le HTML comme le Markdown négocié portent `Vary: Accept`, afin qu'un cache ou un CDN placé devant l'application les distingue. Un article sur une page `noindex` répond avec `X-Robots-Tag: noindex` sous ses deux formes Markdown, puisqu'un corps Markdown n'a nulle part où porter la balise meta — un hébergeur statique ne peut pas envoyer cet en-tête : gardez donc une collection retenue hors d'un build statique.

Ces jumeaux existent pour les assistants d'IA et pour les lecteurs qui veulent la source. Ce ne sont pas un signal de recherche — Google a indiqué ne pas avoir besoin de fichiers Markdown ni de `llms.txt` pour indexer un site — mais chaque article annonce son propre jumeau dans son en-tête avec `<link rel="alternate" type="text/markdown">`, afin qu'un agent lisant le HTML trouve le Markdown sans deviner l'adresse.

## Ce qu'écrit `sovrium build`

Un build statique rend chaque page publique en HTML et, sur demande, les fichiers qui l'entourent :

| Fichier                     | Écrit quand                                             |
| --------------------------- | ------------------------------------------------------- |
| `sitemap.xml`               | `SOVRIUM_GENERATE_SITEMAP=true`                         |
| `sitemap-1.xml`, …          | Le sitemap dépasse 5 000 adresses                       |
| `blog/<slug>.html`, …       | Un enregistrement que le sitemap liste                  |
| `robots.txt`                | `SOVRIUM_GENERATE_ROBOTS=true`                          |
| `llms.txt`, `llms-full.txt` | L'application les servirait                             |
| `<article>.md`              | Chaque article public d'un répertoire de contenu        |

Le build prend son origine dans `SOVRIUM_BASE_URL`, pas dans `BASE_URL`. Le jumeau `.md` de chaque article est écrit à côté de son HTML, à l'adresse à laquelle répond le serveur : un hébergeur statique sert ainsi `/docs/getting-started.md` comme le fait le serveur. Il n'écrit **pas** `feed.xml`, qui n'existe que sur un serveur en cours d'exécution.

Avec le sitemap activé, le build lit les enregistrements de chaque page de collection dans la base de données contre laquelle il s'exécute — `DATABASE_URL`, comme pour `sovrium start` — selon les mêmes règles que le sitemap servi décrit plus haut : une table que tout le monde peut lire, aucune règle de lecture au niveau des lignes, un champ d'adresse lisible par tous, les enregistrements supprimés écartés, les mêmes pages de 5 001 lignes et le même plafond. Il les lit une fois et utilise cette lecture deux fois : chaque enregistrement obtient un `<url>` dans `sitemap.xml`, et la page de chaque enregistrement est rendue et écrite, à l'adresse où elle est listée (`/blog/pricing-change` devient `blog/pricing-change.html`). Chaque adresse annoncée par le sitemap construit est donc un fichier auquel un hébergeur statique peut répondre, et pour les mêmes enregistrements, le `sitemap.xml` construit est le document que sert le serveur en cours d'exécution, `lastmod` compris. Au-delà de 5 000 adresses, le build écrit l'index et ses enfants `sitemap-N.xml` à côté. Les pages d'enregistrement sont écrites telles que rendues, sans la mise en forme des espaces appliquée aux pages déclarées, car une table de milliers de lignes passerait sinon des minutes à les réindenter. Une collection dont la route porte plus d'un paramètre, en dehors de `:lang`, ne liste aucun enregistrement et n'écrit aucune page d'enregistrement. Sans le sitemap, le build ne lit aucun enregistrement et n'écrit aucune page d'enregistrement.

## Pages connexes

- [SEO et métadonnées](/fr/docs/seo-meta) — `noindex`, `canonical` et les balises sociales qu'un robot lit sur chaque page.
- [Publier llms.txt](/fr/docs/llms-txt) — les fichiers d'index et de corpus pour les assistants d'IA.
- [Langues](/fr/docs/languages) — les codes de langue à partir desquels sont construites les alternatives `hreflang`.
- [Collections et markdown](/fr/docs/pages-collections) — répertoires de contenu et pages d'enregistrement.
- [Variables d'environnement](/fr/docs/env-vars) — `BASE_URL` et les variables de build.
