📖 11 min de lecture

SEO pour les sites Astro : le guide complet 2026

Tout ce qu'il faut pour faire remonter un site Astro dans les résultats : métadonnées, sitemaps, données structurées, i18n et Core Web Vitals.

  • Astro
  • Technical SEO

Astro est l’un des meilleurs frameworks que vous puissiez choisir si la recherche organique compte pour vous — et ce n’est pas un hasard. Toute sa philosophie de conception (livrer du HTML, livrer zéro JavaScript par défaut, rendre au moment du build) s’aligne presque parfaitement avec ce que les moteurs de recherche récompensent réellement. Le site que vous lisez en ce moment même est construit avec Astro : tout ce qui suit a donc été éprouvé sur le terrain, ce n’est pas de la théorie.

Ce guide parcourt toute la surface SEO d’un projet Astro : métadonnées et canonicals, sitemaps et robots, données structurées JSON-LD, routage multilingue, Core Web Vitals, ainsi que les pièges qui vous coûtent discrètement des positions. Chaque section s’accompagne de code réel que vous pouvez coller dans votre propre projet.

Pourquoi Astro est excellent pour le SEO

La plupart des problèmes SEO des sites modernes remontent à une cause racine : la page que le crawler télécharge n’est pas la page que l’utilisateur voit. Une route rendue côté client en React ou Next livre un <div id="root"></div> vide et hydrate le vrai contenu plus tard. Google peut exécuter du JavaScript, mais il le fait lors d’une seconde vague différée, avec un budget limité — consultez notre analyse approfondie sur le JavaScript SEO et le rendu pour comprendre ce qui se passe réellement dans ce pipeline.

Astro contourne tout le problème :

PréoccupationSPA typiqueAstro (par défaut)
HTML initialCoquille videContenu entièrement rendu
JavaScript livréTout le bundle de l’appZéro, sauf si vous l’activez
Contenu visible au crawlAprès la vague de renduÀ la première requête
Time to first byteCalcul serveurFichier statique depuis le CDN

L’architecture en îlots (Islands) d’Astro signifie que l’interactivité s’active à la demande, composant par composant. Vous hydratez la seule barre de recherche ou le seul carrousel qui a besoin de JavaScript, et tout le reste demeure du HTML brut, crawlable. La sortie par défaut est statique (SSG), sémantique et immédiatement indexable.

🧑‍💻 Point de vue développeur : le plus gros gain SEO d’Astro est une non-décision. Vous ne faites rien, et vous obtenez du HTML rendu côté serveur sans bundle client. Les autres frameworks vous obligent à vous battre pour ça.

Métadonnées et canonical

Les balises title, les meta descriptions, les URL canoniques et les balises Open Graph sont le minimum vital. L’erreur que commettent la plupart des équipes consiste à les éparpiller sur les pages, si bien qu’elles finissent par diverger. La solution : un unique layout qui reçoit des props et émet un balisage <head> cohérent partout.

Créez src/layouts/BaseLayout.astro :

---
interface Props {
  title: string;
  description: string;
  image?: string;
}

const { title, description, image = "/og-default.png" } = Astro.props;

// Build an absolute canonical from the current URL + your site origin.
const canonical = new URL(Astro.url.pathname, Astro.site).href;
const ogImage = new URL(image, Astro.site).href;
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{title}</title>
    <meta name="description" content={description} />
    <link rel="canonical" href={canonical} />

    <!-- Open Graph -->
    <meta property="og:type" content="website" />
    <meta property="og:title" content={title} />
    <meta property="og:description" content={description} />
    <meta property="og:url" content={canonical} />
    <meta property="og:image" content={ogImage} />

    <!-- Twitter -->
    <meta name="twitter:card" content="summary_large_image" />
  </head>
  <body>
    <slot />
  </body>
</html>

Pour que Astro.site se résolve, définissez-la dans astro.config.mjs :

export default defineConfig({
  site: "https://example.com",
});

Désormais, chaque page se contente de transmettre les données :

---
import BaseLayout from "../layouts/BaseLayout.astro";
---
<BaseLayout
  title="SEO for Astro Sites: The Complete 2026 Guide"
  description="Make your Astro site rank with metadata, sitemaps, and more."
>
  <h1>...</h1>
</BaseLayout>

💡 Astuce : une canonical auto-référente (la page qui pointe vers sa propre URL propre) est la défense la plus efficace contre les problèmes de contenu dupliqué causés par les chaînes de requête, les paramètres de tracking et les variantes avec/sans barre oblique finale.

Sitemap et robots

Un sitemap indique aux moteurs de recherche quelles URL existent et quand elles ont changé. Astro dispose d’une intégration officielle — installez-la et enregistrez-la :

npx astro add sitemap

Cette commande modifie votre config pour obtenir :

import sitemap from "@astrojs/sitemap";

export default defineConfig({
  site: "https://example.com",
  integrations: [sitemap()],
});

Au moment du astro build, vous obtenez maintenant sitemap-index.xml ainsi que des sitemaps enfants paginés dans dist/. L’intégration ne fonctionne que si site est défini — cette origine préfixe chaque URL.

Pour robots.txt, oubliez le fichier statique et utilisez un endpoint dynamique afin que l’URL du sitemap reste synchronisée avec votre config. Créez src/pages/robots.txt.ts :

import type { APIRoute } from "astro";

const robots = (sitemapURL: URL) => `
User-agent: *
Allow: /

Sitemap: ${sitemapURL.href}
`.trim();

export const GET: APIRoute = ({ site }) => {
  const sitemapURL = new URL("sitemap-index.xml", site);
  return new Response(robots(sitemapURL), {
    headers: { "Content-Type": "text/plain" },
  });
};

⚠️ Attention : Disallow dans robots.txt bloque le crawl, pas l’indexation. Une URL interdite mais pointée par des liens externes peut toujours apparaître dans les résultats sous forme de simple lien. Pour garder une page hors de l’index, laissez-la être crawlée et ajoutez plutôt <meta name="robots" content="noindex">.

Données structurées (JSON-LD)

Les données structurées sont ce qui vous permet de décrocher des résultats enrichis : signatures d’articles, fils d’Ariane, accordéons de FAQ, notes en étoiles. Google les lit sous forme de JSON-LD dans une balise script. Le pattern propre en Astro consiste à construire l’objet dans le frontmatter et à le sérialiser dans le layout.

Dans BaseLayout.astro, acceptez une prop schema optionnelle et émettez-la avec set:html pour que les guillemets ne soient pas échappés :

---
interface Props {
  title: string;
  description: string;
  schema?: Record<string, unknown>;
}
const { title, description, schema } = Astro.props;
---
<head>
  <!-- ...other tags... -->
  {schema && (
    <script
      type="application/ld+json"
      set:html={JSON.stringify(schema)}
    />
  )}
</head>

Une page d’article de blog transmettrait un objet Article :

---
const schema = {
  "@context": "https://schema.org",
  "@type": "Article",
  headline: "SEO for Astro Sites: The Complete 2026 Guide",
  datePublished: "2026-06-21",
  author: { "@type": "Person", name: "Your Name" },
};
---
<BaseLayout title="..." description="..." schema={schema}>

💡 Astuce : écrire les objets schema à la main est source d’erreurs. Notre générateur de Schema produit un JSON-LD valide pour les types courants, que vous pouvez coller directement dans la prop. Revérifiez toujours le résultat dans le test des résultats enrichis de Google (Rich Results Test).

Multilingue (i18n)

Si vous servez plusieurs langues, la règle est simple et sans pitié : chaque version linguistique d’une page doit déclarer toutes les autres versions via hreflang, y compris une auto-référence. Commettez une erreur ici et Google servira la mauvaise langue ou traitera les variantes comme des doublons.

Configurez le routage dans astro.config.mjs :

export default defineConfig({
  site: "https://example.com",
  i18n: {
    defaultLocale: "en",
    locales: ["en", "fr", "ja"],
    routing: { prefixDefaultLocale: true },
  },
});

Avec prefixDefaultLocale: true, le contenu vit sous /en/, /fr/, /ja/ — pas de racine ambiguë qui entrerait en concurrence avec un chemin de langue. Émettez ensuite le cluster hreflang dans votre <head> :

---
const locales = ["en", "fr", "ja"];
// Strip the current locale prefix to get the shared path segment.
const path = Astro.url.pathname.replace(/^\/[a-z]{2}/, "");
---
{locales.map((loc) => (
  <link
    rel="alternate"
    hreflang={loc}
    href={new URL(`/${loc}${path}`, Astro.site).href}
  />
))}
<link
  rel="alternate"
  hreflang="x-default"
  href={new URL(`/en${path}`, Astro.site).href}
/>

L’entrée x-default indique à Google quelle version afficher lorsqu’aucune langue ne correspond à l’utilisateur. Conservez les traductions sous forme de fichiers parallèles (content/blog/en/post.mdx, content/blog/fr/post.mdx) pour que le calcul de chemin ci-dessus reste trivial.

Core Web Vitals

La sortie statique d’Astro vous donne une longueur d’avance : aucun coût d’hydratation, aucun framework client provoquant des décalages de mise en page, du HTML servi directement depuis un CDN. Cela couvre gratuitement la majeure partie du Largest Contentful Paint (LCP) et de l’Interaction to Next Paint (INP). Les deux choses qui continuent de vous piéger sont les images et les polices.

Utilisez le composant <Image /> intégré d’Astro — il génère des formats modernes, redimensionne et impose des dimensions :

---
import { Image } from "astro:assets";
import hero from "../assets/hero.png";
---
<Image
  src={hero}
  alt="Descriptive alt text"
  width={1200}
  height={630}
  format="webp"
  loading="eager"
/>

Une courte checklist pour rester dans la zone verte :

  • Définissez explicitement width/height sur chaque image pour éviter les décalages de mise en page (CLS).
  • Utilisez loading="eager" pour l’image LCP, lazy pour tout ce qui est sous la ligne de flottaison.
  • Hébergez vous-même les polices et ajoutez font-display: swap pour que le texte s’affiche avant le chargement de la police.
  • Préchargez l’unique fichier de police le plus important ; ne les préchargez pas tous.
  • N’appliquez aucune directive client:* à du contenu visible au premier rendu.
MétriqueCe qu’elle mesureComportement par défaut d’Astro
LCPTemps de peinture du plus grand élémentSolide — HTML statique, servi par CDN
CLSStabilité visuelleSolide si les images ont des dimensions
INPRéactivité aux interactionsExcellent — aucune hydratation par défaut

Pièges courants

Voici les erreurs qui passent les tests en local et plombent quand même le classement :

  • Le contenu client:only est invisible pour les crawlers. Un composant client:only ne rend rien côté serveur — il n’y a aucun HTML de repli. Si du contenu important s’y trouve, il ne sera pas indexé. Utilisez client:load (qui rend toujours côté serveur) ou, mieux, gardez le contenu en .astro brut.
  • Incohérence de barre oblique finale. L’option trailingSlash d’Astro ("always", "never", "ignore") doit correspondre à la façon dont votre hébergeur sert les fichiers et à ce qu’utilisent vos liens internes. Un décalage produit /page et /page/ comme deux URL distinctes, ce qui divise le link equity. Choisissez-en une, définissez-la dans la config, et faites en sorte que chaque lien interne s’y conforme.
  • Config site manquante. Sans site défini, les canonicals et le sitemap se cassent silencieusement ou produisent des URL relatives. C’est la cause la plus fréquente d’un sitemap vide ou invalide.
  • Oublier le noindex sur les pages pauvres. Les pages d’archives de tags et de pagination n’apportent souvent aucune valeur. Ajoutez-leur une balise meta noindex plutôt que de les laisser diluer votre budget de crawl.
  • S’appuyer sur les redirections pour la canonicalisation. Une balise canonical est une suggestion ; un 301 est une directive. Pour les vrais doublons (HTTP vs HTTPS, www vs non-www), imposez la redirection au niveau de l’hébergeur ou du CDN — ne vous reposez pas sur la seule balise canonical.

⚠️ Attention : vérifiez toujours avec l’artefact réel, pas avec le serveur de dev. Lancez astro build et inspectez dist/ — ouvrez le HTML généré et confirmez que votre contenu, vos balises meta et votre JSON-LD sont réellement présents dans la sortie statique. astro dev peut masquer des différences de rendu.

Pour conclure

Astro vous offre une base que la plupart des frameworks vous obligent à construire à la main : du HTML rendu côté serveur, zéro JavaScript par défaut et une sortie statique rapide qu’adorent à la fois les crawlers et les Core Web Vitals. Votre travail consiste à empiler par-dessus les signaux délibérés — un layout unique qui injecte des métadonnées et des canonicals cohérents, un sitemap et un endpoint robots dynamique, du JSON-LD pour les résultats enrichis, des clusters hreflang corrects pour chaque langue, ainsi qu’une gestion rigoureuse des images et des polices.

Faites cela bien et vous aurez éliminé presque toutes les raisons techniques pour lesquelles un moteur de recherche pourrait vous classer plus bas.

Prochaines étapes :