[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f1QmxTvmelt-0w96r6mt2OQ6JUfSeD4Oqqe6MJ4YL7b4":3,"$fpNdMZ8aO2L8PVXkwc1-6_9sffa2h5czkmz83gqm0Rdk":25},{"_id":4,"slug":5,"__v":6,"author":7,"body":8,"canonical":9,"category":10,"createdAt":11,"date":12,"description":13,"htmlContent":14,"image":15,"imageAlt":15,"readingTime":16,"tags":17,"title":23,"updatedAt":24},"69de570daa6b273b0cb90dc2","google-lists-9-scenarios-that-explain-how-it-picks-canonical-urls-via-sejournal-martinibuster",0,"Equipe Seogard","John Mueller vient de publier une liste de neuf scénarios concrets qui expliquent comment Google sélectionne l'URL canonique d'un contenu. Ce n'est pas une nouveauté théorique — c'est la première fois que Google formalise aussi clairement les cas de figure où votre `rel=canonical` sera ignoré au profit d'un autre signal. Pour quiconque gère un site au-delà de quelques centaines de pages, chaque scénario décrit un piège technique réel.\n\n## Le `rel=canonical` est un hint, pas une directive\n\nLa confusion persiste : beaucoup de développeurs traitent `rel=canonical` comme une instruction ferme. C'est faux. Google le classe explicitement comme un **signal** parmi d'autres, au même titre que les redirections, les sitemaps, les liens internes et le contenu de la page elle-même.\n\nLa [documentation officielle de Google sur la canonicalisation](https://developers.google.com/search/docs/crawling-indexing/canonicalization) utilise le terme \"hint\" — un indice. Quand plusieurs signaux se contredisent, Google tranche selon sa propre logique. Et c'est précisément ce que les 9 scénarios de Mueller détaillent.\n\n### Pourquoi Google ignore-t-il vos canonicals ?\n\nLe moteur agrège plusieurs signaux pour déterminer quelle URL \"mérite\" d'être la référence :\n\n- La balise `rel=canonical` dans le `\u003Chead>`\n- L'en-tête HTTP `Link: rel=canonical`\n- Les redirections 301/302\n- Les URL déclarées dans le sitemap XML\n- Les liens internes (quelle variante reçoit le plus de liens ?)\n- Le contenu de la page (identique, quasi-identique, ou différent ?)\n- Le protocole (HTTP vs HTTPS)\n- La présence ou absence de `www`\n\nQuand ces signaux s'alignent, tout va bien. Quand ils divergent, Google choisit — et son choix ne sera pas toujours celui que vous attendez.\n\n### Le cas classique du e-commerce multi-facettes\n\nPrenez un site e-commerce de 18 000 pages produit, avec des filtres de recherche qui génèrent des URL paramétrées. La page produit \"Chaussures running homme Nike\" existe sous :\n\n```html\n\u003C!-- Page principale -->\n\u003Clink rel=\"canonical\" href=\"https://shop.example.fr/chaussures/running-homme-nike\" />\n\n\u003C!-- Variantes paramétrées qui pointent vers la même canonical -->\nhttps://shop.example.fr/chaussures/running-homme-nike?color=noir\nhttps://shop.example.fr/chaussures/running-homme-nike?size=42&color=noir\nhttps://shop.example.fr/chaussures?brand=nike&category=running&gender=homme\n```\n\nSi le sitemap XML déclare l'URL paramétrée, si les liens internes pointent majoritairement vers la version avec paramètres, et si la navigation à facettes génère plus de maillage vers `?brand=nike&category=running` que vers le slug propre — Google peut parfaitement ignorer votre canonical et indexer la version paramétrée.\n\n## Les 9 scénarios décortiqués techniquement\n\nReprenons les scénarios listés par Mueller et analysons chacun avec le niveau de détail qu'un Lead SEO attend.\n\n### Scénario 1 : Redirection vs canonical contradictoire\n\nL'URL A redirige en 301 vers l'URL B, mais l'URL B déclare l'URL C comme canonical. Google doit trancher entre le signal de redirection (A → B) et le signal canonical (B → C). Dans la majorité des cas observés, **la chaîne de redirection l'emporte** si elle est cohérente et ancienne. Mais si C est clairement la page la plus liée et la plus présente dans le sitemap, Google peut suivre la canonical.\n\n**Recommandation** : ne créez jamais de contradiction entre vos redirections et vos canonicals. Vérifiez avec Screaming Frog en lançant un crawl avec le mode \"Follow canonical\" activé :\n\n```bash\n# Export Screaming Frog CLI pour détecter les conflits redirect/canonical\nscreamingfrogseospider --crawl https://shop.example.fr \\\n  --headless \\\n  --export-tabs \"Redirects:All,Canonicals:All\" \\\n  --output-folder /tmp/audit-canonicals/\n```\n\nCroisez ensuite les deux exports : toute URL qui apparaît comme destination de redirect ET dont la canonical pointe ailleurs est un conflit à résoudre.\n\n### Scénario 2 : HTTPS vs HTTP\n\nGoogle préfère HTTPS. Si les deux versions d'une page existent et que la version HTTP ne redirige pas proprement vers HTTPS, Google choisira HTTPS comme canonical — même si la page HTTP déclare une canonical vers elle-même.\n\nCe scénario est courant après une [migration HTTP vers HTTPS](/blog/migration-http-vers-https-checklist-seo-complete) incomplète. Le piège : les redirections sont en place sur le serveur principal, mais un CDN ou un reverse proxy sert encore du HTTP sur certains chemins.\n\n### Scénario 3 : www vs non-www\n\nIdentique au scénario HTTPS/HTTP en logique. Google choisit la variante la plus cohérente avec le reste des signaux. Si votre sitemap déclare `www.example.fr`, que vos liens internes utilisent `www.example.fr`, mais que votre canonical dit `example.fr` — vous envoyez des signaux contradictoires.\n\n### Scénario 4 : URL dans le sitemap vs canonical déclarée\n\nUn signal sous-estimé. Google accorde du poids aux URL listées dans le sitemap XML. Si votre sitemap contient `https://shop.example.fr/produit/123` mais que la page déclare une canonical vers `https://shop.example.fr/produit/chaussure-nike-air`, Google doit résoudre le conflit.\n\nVoici une config Nginx qui force la cohérence entre URL servie et sitemap en redirigeant les variantes numériques :\n\n```nginx\n# Redirection des anciennes URL numériques vers les slugs SEO-friendly\nlocation ~ ^/produit/(\\d+)$ {\n    # Lookup dans une map ou un backend pour résoudre l'ID vers le slug\n    # Ici, simplifié avec une map statique\n    set $new_slug \"\";\n    if ($1 = \"123\") {\n        set $new_slug \"chaussure-nike-air\";\n    }\n    if ($new_slug != \"\") {\n        return 301 https://shop.example.fr/produit/$new_slug;\n    }\n    # Si pas de mapping trouvé, 404\n    return 404;\n}\n```\n\n### Scénario 5 : Contenu dupliqué sur des URL différentes\n\nLe scénario le plus fréquent à grande échelle. Deux URL servent un contenu identique ou quasi-identique sans aucune balise canonical, sans redirection, et toutes deux sont dans le sitemap. Google doit deviner laquelle est la \"bonne\".\n\nLes signaux de départage : volume de liens internes, ancienneté de l'URL, liens externes, performances de l'URL dans les résultats de recherche.\n\n### Scénario 6 : Canonical pointant vers une page 404 ou non-indexable\n\nSi votre `rel=canonical` pointe vers une URL qui retourne un 404, un `noindex`, ou une 5xx, Google l'ignore et sélectionne l'URL courante (ou une autre variante) comme canonical. C'est un scénario fréquent post-[refonte de site](/blog/refonte-de-site-les-20-verifications-seo-indispensables) quand les anciennes URL canoniques n'existent plus.\n\n### Scénario 7 : Canonical auto-référente absente\n\nQuand une page ne déclare aucune canonical, Google utilise les autres signaux pour en déduire une. L'absence de canonical auto-référente n'est pas une erreur en soi, mais elle laisse plus de latitude à Google pour choisir une URL que vous n'avez pas prévue — surtout si des paramètres de tracking (`utm_source`, `fbclid`) créent des variantes.\n\n### Scénario 8 : Pages paginées avec canonicals mal configurées\n\nLes pages 2, 3, 4… d'une liste produit qui déclarent toutes une canonical vers la page 1. Google a explicitement dit que ce pattern est incorrect : chaque page paginée doit avoir une canonical auto-référente. Sinon, seule la page 1 sera indexée, et les produits des pages suivantes disparaissent de l'index.\n\n### Scénario 9 : Hreflang et canonical en conflit\n\nDans un [contexte multilingue](/blog/seo-multilingue-architecture-technique-optimale), la page FR déclare une canonical vers elle-même ET un hreflang vers la page EN. Mais la page EN déclare une canonical vers la page FR. Google ne peut pas résoudre cette boucle. Le résultat : il ignore le hreflang et choisit la canonical qu'il estime la plus pertinente — souvent la version dans la langue la plus populaire pour la requête.\n\n## Diagnostic technique : détecter les conflits de canonical à grande échelle\n\nSur un site de 500 pages, un audit manuel suffit. Sur 15 000 pages, il faut automatiser. Voici un pipeline de diagnostic complet.\n\n### Étape 1 : Extraire les canonicals déclarées via crawl\n\nScreaming Frog reste l'outil de référence pour l'extraction. Mais sur de gros volumes, un crawl programmatique avec Python est plus flexible :\n\n```python\nimport requests\nfrom bs4 import BeautifulSoup\nfrom urllib.parse import urlparse\nimport csv\n\ndef audit_canonicals(urls: list[str], output_file: str):\n    \"\"\"\n    Pour chaque URL, vérifie la cohérence entre :\n    - l'URL crawlée\n    - la canonical HTML\n    - la canonical HTTP header\n    - le status code\n    \"\"\"\n    results = []\n    \n    for url in urls:\n        try:\n            resp = requests.get(url, timeout=10, allow_redirects=True)\n            final_url = resp.url  # URL après redirections\n            status = resp.status_code\n            \n            # Canonical depuis le header HTTP\n            link_header = resp.headers.get(\"Link\", \"\")\n            http_canonical = None\n            if 'rel=\"canonical\"' in link_header:\n                http_canonical = link_header.split(\";\")[0].strip(\"\u003C>\")\n            \n            # Canonical depuis le HTML\n            soup = BeautifulSoup(resp.text, \"html.parser\")\n            link_tag = soup.find(\"link\", {\"rel\": \"canonical\"})\n            html_canonical = link_tag[\"href\"] if link_tag else None\n            \n            # Détection des conflits\n            conflict = \"NONE\"\n            if html_canonical and http_canonical and html_canonical != http_canonical:\n                conflict = \"HTML_VS_HTTP_HEADER\"\n            elif html_canonical and html_canonical != final_url:\n                conflict = \"CANONICAL_DIFFERS_FROM_URL\"\n            elif not html_canonical:\n                conflict = \"MISSING_CANONICAL\"\n            \n            results.append({\n                \"requested_url\": url,\n                \"final_url\": final_url,\n                \"status\": status,\n                \"html_canonical\": html_canonical,\n                \"http_canonical\": http_canonical,\n                \"conflict\": conflict,\n            })\n        except Exception as e:\n            results.append({\n                \"requested_url\": url,\n                \"final_url\": None,\n                \"status\": \"ERROR\",\n                \"html_canonical\": None,\n                \"http_canonical\": None,\n                \"conflict\": f\"FETCH_ERROR: {e}\",\n            })\n    \n    with open(output_file, \"w\", newline=\"\") as f:\n        writer = csv.DictWriter(f, fieldnames=results[0].keys())\n        writer.writeheader()\n        writer.writerows(results)\n    \n    conflicts = [r for r in results if r[\"conflict\"] != \"NONE\"]\n    print(f\"Audit terminé : {len(conflicts)} conflits sur {len(results)} URLs\")\n\n# Usage avec les URLs extraites du sitemap\nfrom xml.etree import ElementTree\nsitemap_resp = requests.get(\"https://shop.example.fr/sitemap.xml\")\ntree = ElementTree.fromstring(sitemap_resp.content)\nns = {\"s\": \"http://www.sitemaps.org/schemas/sitemap/0.9\"}\nurls = [loc.text for loc in tree.findall(\".//s:loc\", ns)]\n\naudit_canonicals(urls, \"canonical_audit.csv\")\n```\n\nCe script détecte les quatre cas problématiques : canonical manquante, canonical différente de l'URL servie, conflit entre canonical HTML et header HTTP, erreur de fetch.\n\n### Étape 2 : Croiser avec Google Search Console\n\nL'onglet \"Indexation des pages\" dans Google Search Console montre les URL que Google a choisies comme canoniques. Exportez ce rapport et croisez-le avec votre audit :\n\n- **URL déclarée canonical = URL indexée par Google** → OK\n- **URL déclarée canonical ≠ URL indexée par Google** → Google a ignoré votre canonical\n- **URL non indexée, raison \"Autre page canonical\"** → Google a élu une autre URL\n\nCe croisement révèle les vrais désaccords entre votre intention et le choix de Google. Sur un site e-commerce de 15 000 pages, il n'est pas rare de trouver 8 à 15% d'URLs où Google a choisi une canonical différente de celle déclarée.\n\n### Étape 3 : Monitoring continu\n\nL'audit ponctuel ne suffit pas. Une mise à jour de template, un déploiement qui casse le SSR, un [changement de framework](/blog/changer-de-framework-next-js-vers-nuxt-ou-l-inverse-sans-perte-seo) — tout cela peut introduire des régressions sur les canonicals du jour au lendemain. Un outil de monitoring comme Seogard détecte ces régressions en temps réel : si une canonical disparaît ou change sur un batch de pages, vous recevez une alerte avant que Google ne recrawle.\n\n## Scénario réel : migration SSR et canonicals cassées\n\nUn média en ligne de 12 000 articles migre d'un [headless CMS](/blog/headless-cms-et-seo-avantages-et-risques-techniques) avec rendu côté client (CSR) vers un setup Next.js avec SSR. Avant la migration, les canonicals étaient injectées côté client via JavaScript. Googlebot les rendait correctement — le [rendering budget](/blog/rendering-budget-de-google-combien-de-javascript-est-trop) étant suffisant pour ce volume.\n\nAprès la migration vers SSR, l'équipe dev a oublié de porter le composant `\u003CHead>` qui injectait la balise canonical. Résultat : pendant 11 jours, les 12 000 pages ont été servies sans aucune canonical déclarée.\n\n### L'impact observé\n\n- **Jours 1-3** : rien de visible. Google n'a pas encore recrawlé massivement.\n- **Jours 4-7** : le crawl rate augmente (Google détecte le changement de stack et recrawle plus agressivement). Les pages sont indexées sans canonical.\n- **Jours 8-11** : Google commence à sélectionner ses propres canonicals. Pour 1 400 articles, il choisit des URL avec paramètres de tracking (`?utm_source=newsletter`) qui avaient été crawlées via des liens dans des emails indexés par des webmails publics.\n- **Impact trafic** : -23% de trafic organique sur les 1 400 articles affectés, car Google a dilué les signaux entre la \"vraie\" URL et la variante UTM.\n\n### La correction\n\n```typescript\n// components/SEOHead.tsx — Next.js App Router\nimport { headers } from 'next/headers';\n\nexport default function SEOHead({ \n  title, \n  description, \n  canonicalPath \n}: {\n  title: string;\n  description: string;\n  canonicalPath: string;\n}) {\n  // Construire la canonical à partir du path, jamais depuis l'URL courante\n  // pour éviter d'inclure des paramètres de tracking\n  const baseUrl = 'https://media.example.fr';\n  const canonical = `${baseUrl}${canonicalPath}`;\n\n  return (\n    \u003C>\n      \u003Ctitle>{title}\u003C/title>\n      \u003Cmeta name=\"description\" content={description} />\n      \u003Clink rel=\"canonical\" href={canonical} />\n    \u003C/>\n  );\n}\n```\n\nLe point clé : la canonical est construite à partir d'un `canonicalPath` stocké en base de données, jamais dérivée de l'URL de la requête HTTP entrante. C'est la seule façon d'éviter que des paramètres de query string se glissent dans la canonical.\n\nAprès correction et soumission d'un recrawl via Search Console (Inspection d'URL > Demander l'indexation pour les pages critiques, puis un nouveau sitemap ping), le trafic est revenu à son niveau initial en 18 jours.\n\n## Les signaux que Google pondère le plus (par ordre d'importance observé)\n\nGoogle n'a jamais publié de hiérarchie officielle des signaux de canonicalisation. Mais en croisant les déclarations de Mueller, la documentation officielle, et les observations à grande échelle sur des centaines de sites, un ordre de priorité se dessine :\n\n1. **Redirections 301** — le signal le plus fort. Une 301 est quasi-toujours respectée comme signal canonical.\n2. **Canonical HTML + header HTTP cohérents** — quand les deux sont présents et concordants, Google les suit dans la grande majorité des cas.\n3. **URL dans le sitemap XML** — poids modéré, mais c'est un départageur quand les autres signaux sont ambigus.\n4. **Liens internes** — la variante qui reçoit le plus de maillage interne est favorisée. C'est un signal souvent sous-estimé.\n5. **HTTPS > HTTP** — préférence systématique sauf si tous les autres signaux pointent vers HTTP.\n6. **Liens externes** — si une variante a significativement plus de backlinks, elle peut l'emporter.\n\n### Le piège des redirections 302\n\nLes 302 (redirect temporaire) sont un cas ambivalent. Google a confirmé que les 302 transmettent les signaux de ranking comme les 301 dans la plupart des cas. Mais pour la canonicalisation spécifiquement, une 302 longue durée (plus de quelques semaines) sera traitée comme une 301 de facto. Le risque : pendant la période d'ambiguïté, Google peut hésiter entre les deux URL.\n\nRègle simple : si la redirection est permanente, utilisez 301. Toujours. Les [tests A/B](/blog/a-b-testing-seo-tester-sans-penaliser-le-referencement) sont le seul cas légitime de 302 durable.\n\n## Edge cases et trade-offs à connaître\n\n### Pages avec contenu rendu en JavaScript\n\nSi votre canonical est injectée par JavaScript et que Googlebot ne rend pas la page (timeout, erreur JS, dépassement du rendering budget), la canonical n'existe pas pour Google. C'est le scénario 7 (canonical absente) par défaut.\n\nVérifiez systématiquement le rendu Googlebot via le test d'URL enrichie de Search Console ou via Chrome DevTools en désactivant JavaScript :\n\n```bash\n# Vérifier ce que Googlebot voit sans JS rendering\ncurl -s -A \"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)\" \\\n  https://shop.example.fr/produit/chaussure-nike-air \\\n  | grep -i \"canonical\"\n```\n\nSi cette commande ne retourne rien, votre canonical dépend du rendering JavaScript. C'est un risque si vous êtes sur une stack [API-first](/blog/api-first-et-seo-servir-du-contenu-crawlable-depuis-une-api) sans SSR.\n\n### Canonicals cross-domain\n\nVous pouvez déclarer une canonical vers un autre domaine (syndication de contenu, par exemple). Google respecte ce signal, mais avec plus de prudence. Le domaine cible doit être accessible, retourner un 200, et le contenu doit être suffisamment similaire. Si le domaine cible bloque Googlebot ou retourne un contenu différent, Google ignorera la canonical cross-domain.\n\n### Canonical et pages indexées via Google Discover\n\nPour les sites médias qui reçoivent du trafic [Google Discover](/blog/inside-google-discover-20-pipelines-42-million-cards-and-what-they-mean-for-publishers), la canonical est encore plus critique. Discover utilise l'URL canonique pour décider quelle version afficher dans le feed. Une canonical mal configurée peut rediriger le trafic Discover vers une URL qui n'est pas celle optimisée pour l'engagement mobile.\n\n## Checklist opérationnelle post-Mueller\n\nVoici les vérifications à implémenter immédiatement après lecture de ces 9 scénarios :\n\n**Au niveau du template :**\n- Chaque page a une canonical auto-référente, sauf si elle est explicitement un doublon.\n- La canonical est dans le HTML initial (pas injectée par JS uniquement).\n- La canonical utilise le même protocole (HTTPS) et le même sous-domaine (www ou non) que l'URL servie.\n\n**Au niveau du serveur :**\n- Les redirections 301 et les canonicals pointent vers la même URL finale.\n- Le header HTTP `Link: rel=canonical` est cohérent avec la balise HTML (ou absent — les deux ne sont pas nécessaires simultanément).\n- Les paramètres de tracking sont exclus des canonicals.\n\n**Au niveau du sitemap :**\n- Seules les URL canoniques sont dans le sitemap. Jamais de variantes paramétrées ou de doublons.\n- Le sitemap est à jour après chaque [mise à jour majeure](/blog/google-confirms-march-2026-core-update-is-complete-via-sejournal-mattgsouthern) ou refonte.\n\n**Au niveau du monitoring :**\n- Croisement régulier (hebdomadaire minimum) entre les canonicals déclarées et celles choisies par Google dans Search Console.\n- Alertes automatiques si une canonical disparaît ou change sur plus de 1% des pages.\n\nLa canonicalisation n'est pas un sujet qu'on configure une fois et qu'on oublie. Chaque déploiement, chaque migration, chaque changement de CDN peut introduire une régression silencieuse. Les 9 scénarios de Mueller ne sont pas des cas théoriques — ce sont des bugs que Seogard détecte quotidiennement sur les sites qu'il monitore. La seule protection fiable, c'est un monitoring continu qui compare l'intention (votre canonical déclarée) avec la réalité (ce que Google a effectivement indexé).\n```","https://seogard.io/blog/google-lists-9-scenarios-that-explain-how-it-picks-canonical-urls-via-sejournal-martinibuster","Actualités SEO","2026-04-14T15:02:37.538Z","2026-04-14","Analyse technique des 9 scénarios où Google choisit une URL canonique. Code, configs et stratégies pour garder le contrôle de vos canonicals.","\u003Cp>John Mueller vient de publier une liste de neuf scénarios concrets qui expliquent comment Google sélectionne l'URL canonique d'un contenu. Ce n'est pas une nouveauté théorique — c'est la première fois que Google formalise aussi clairement les cas de figure où votre \u003Ccode>rel=canonical\u003C/code> sera ignoré au profit d'un autre signal. Pour quiconque gère un site au-delà de quelques centaines de pages, chaque scénario décrit un piège technique réel.\u003C/p>\n\u003Ch2>Le \u003Ccode>rel=canonical\u003C/code> est un hint, pas une directive\u003C/h2>\n\u003Cp>La confusion persiste : beaucoup de développeurs traitent \u003Ccode>rel=canonical\u003C/code> comme une instruction ferme. C'est faux. Google le classe explicitement comme un \u003Cstrong>signal\u003C/strong> parmi d'autres, au même titre que les redirections, les sitemaps, les liens internes et le contenu de la page elle-même.\u003C/p>\n\u003Cp>La \u003Ca href=\"https://developers.google.com/search/docs/crawling-indexing/canonicalization\">documentation officielle de Google sur la canonicalisation\u003C/a> utilise le terme \"hint\" — un indice. Quand plusieurs signaux se contredisent, Google tranche selon sa propre logique. Et c'est précisément ce que les 9 scénarios de Mueller détaillent.\u003C/p>\n\u003Ch3>Pourquoi Google ignore-t-il vos canonicals ?\u003C/h3>\n\u003Cp>Le moteur agrège plusieurs signaux pour déterminer quelle URL \"mérite\" d'être la référence :\u003C/p>\n\u003Cul>\n\u003Cli>La balise \u003Ccode>rel=canonical\u003C/code> dans le \u003Ccode>&#x3C;head>\u003C/code>\u003C/li>\n\u003Cli>L'en-tête HTTP \u003Ccode>Link: rel=canonical\u003C/code>\u003C/li>\n\u003Cli>Les redirections 301/302\u003C/li>\n\u003Cli>Les URL déclarées dans le sitemap XML\u003C/li>\n\u003Cli>Les liens internes (quelle variante reçoit le plus de liens ?)\u003C/li>\n\u003Cli>Le contenu de la page (identique, quasi-identique, ou différent ?)\u003C/li>\n\u003Cli>Le protocole (HTTP vs HTTPS)\u003C/li>\n\u003Cli>La présence ou absence de \u003Ccode>www\u003C/code>\u003C/li>\n\u003C/ul>\n\u003Cp>Quand ces signaux s'alignent, tout va bien. Quand ils divergent, Google choisit — et son choix ne sera pas toujours celui que vous attendez.\u003C/p>\n\u003Ch3>Le cas classique du e-commerce multi-facettes\u003C/h3>\n\u003Cp>Prenez un site e-commerce de 18 000 pages produit, avec des filtres de recherche qui génèrent des URL paramétrées. La page produit \"Chaussures running homme Nike\" existe sous :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">&#x3C;!-- Page principale -->\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"color:#85E89D\">link\u003C/span>\u003Cspan style=\"color:#B392F0\"> rel\u003C/span>\u003Cspan style=\"color:#E1E4E8\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"canonical\"\u003C/span>\u003Cspan style=\"color:#B392F0\"> href\u003C/span>\u003Cspan style=\"color:#E1E4E8\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"https://shop.example.fr/chaussures/running-homme-nike\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> />\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">&#x3C;!-- Variantes paramétrées qui pointent vers la même canonical -->\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">https://shop.example.fr/chaussures/running-homme-nike?color=noir\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">https://shop.example.fr/chaussures/running-homme-nike?size=42&#x26;color=noir\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">https://shop.example.fr/chaussures?brand=nike&#x26;category=running&#x26;gender=homme\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Si le sitemap XML déclare l'URL paramétrée, si les liens internes pointent majoritairement vers la version avec paramètres, et si la navigation à facettes génère plus de maillage vers \u003Ccode>?brand=nike&#x26;category=running\u003C/code> que vers le slug propre — Google peut parfaitement ignorer votre canonical et indexer la version paramétrée.\u003C/p>\n\u003Ch2>Les 9 scénarios décortiqués techniquement\u003C/h2>\n\u003Cp>Reprenons les scénarios listés par Mueller et analysons chacun avec le niveau de détail qu'un Lead SEO attend.\u003C/p>\n\u003Ch3>Scénario 1 : Redirection vs canonical contradictoire\u003C/h3>\n\u003Cp>L'URL A redirige en 301 vers l'URL B, mais l'URL B déclare l'URL C comme canonical. Google doit trancher entre le signal de redirection (A → B) et le signal canonical (B → C). Dans la majorité des cas observés, \u003Cstrong>la chaîne de redirection l'emporte\u003C/strong> si elle est cohérente et ancienne. Mais si C est clairement la page la plus liée et la plus présente dans le sitemap, Google peut suivre la canonical.\u003C/p>\n\u003Cp>\u003Cstrong>Recommandation\u003C/strong> : ne créez jamais de contradiction entre vos redirections et vos canonicals. Vérifiez avec Screaming Frog en lançant un crawl avec le mode \"Follow canonical\" activé :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Export Screaming Frog CLI pour détecter les conflits redirect/canonical\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">screamingfrogseospider\u003C/span>\u003Cspan style=\"color:#79B8FF\"> --crawl\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> https://shop.example.fr\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  --headless\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  --export-tabs\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Redirects:All,Canonicals:All\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  --output-folder\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /tmp/audit-canonicals/\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Croisez ensuite les deux exports : toute URL qui apparaît comme destination de redirect ET dont la canonical pointe ailleurs est un conflit à résoudre.\u003C/p>\n\u003Ch3>Scénario 2 : HTTPS vs HTTP\u003C/h3>\n\u003Cp>Google préfère HTTPS. Si les deux versions d'une page existent et que la version HTTP ne redirige pas proprement vers HTTPS, Google choisira HTTPS comme canonical — même si la page HTTP déclare une canonical vers elle-même.\u003C/p>\n\u003Cp>Ce scénario est courant après une \u003Ca href=\"/blog/migration-http-vers-https-checklist-seo-complete\">migration HTTP vers HTTPS\u003C/a> incomplète. Le piège : les redirections sont en place sur le serveur principal, mais un CDN ou un reverse proxy sert encore du HTTP sur certains chemins.\u003C/p>\n\u003Ch3>Scénario 3 : www vs non-www\u003C/h3>\n\u003Cp>Identique au scénario HTTPS/HTTP en logique. Google choisit la variante la plus cohérente avec le reste des signaux. Si votre sitemap déclare \u003Ccode>www.example.fr\u003C/code>, que vos liens internes utilisent \u003Ccode>www.example.fr\u003C/code>, mais que votre canonical dit \u003Ccode>example.fr\u003C/code> — vous envoyez des signaux contradictoires.\u003C/p>\n\u003Ch3>Scénario 4 : URL dans le sitemap vs canonical déclarée\u003C/h3>\n\u003Cp>Un signal sous-estimé. Google accorde du poids aux URL listées dans le sitemap XML. Si votre sitemap contient \u003Ccode>https://shop.example.fr/produit/123\u003C/code> mais que la page déclare une canonical vers \u003Ccode>https://shop.example.fr/produit/chaussure-nike-air\u003C/code>, Google doit résoudre le conflit.\u003C/p>\n\u003Cp>Voici une config Nginx qui force la cohérence entre URL servie et sitemap en redirigeant les variantes numériques :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Redirection des anciennes URL numériques vers les slugs SEO-friendly\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">location\u003C/span>\u003Cspan style=\"color:#F97583\"> ~\u003C/span>\u003Cspan style=\"color:#DBEDFF\"> ^/produit/(\\d+)$ \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Lookup dans une map ou un backend pour résoudre l'ID vers le slug\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Ici, simplifié avec une map statique\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    set \u003C/span>\u003Cspan style=\"color:#E1E4E8\">$new_slug \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ($1 \u003C/span>\u003Cspan style=\"color:#F97583\">= \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"123\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        set \u003C/span>\u003Cspan style=\"color:#E1E4E8\">$new_slug \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"chaussure-nike-air\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ($new_slug \u003C/span>\u003Cspan style=\"color:#F97583\">!= \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        return\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 301\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> https://shop.example.fr/produit/$new_slug;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Si pas de mapping trouvé, 404\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 404\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Ch3>Scénario 5 : Contenu dupliqué sur des URL différentes\u003C/h3>\n\u003Cp>Le scénario le plus fréquent à grande échelle. Deux URL servent un contenu identique ou quasi-identique sans aucune balise canonical, sans redirection, et toutes deux sont dans le sitemap. Google doit deviner laquelle est la \"bonne\".\u003C/p>\n\u003Cp>Les signaux de départage : volume de liens internes, ancienneté de l'URL, liens externes, performances de l'URL dans les résultats de recherche.\u003C/p>\n\u003Ch3>Scénario 6 : Canonical pointant vers une page 404 ou non-indexable\u003C/h3>\n\u003Cp>Si votre \u003Ccode>rel=canonical\u003C/code> pointe vers une URL qui retourne un 404, un \u003Ccode>noindex\u003C/code>, ou une 5xx, Google l'ignore et sélectionne l'URL courante (ou une autre variante) comme canonical. C'est un scénario fréquent post-\u003Ca href=\"/blog/refonte-de-site-les-20-verifications-seo-indispensables\">refonte de site\u003C/a> quand les anciennes URL canoniques n'existent plus.\u003C/p>\n\u003Ch3>Scénario 7 : Canonical auto-référente absente\u003C/h3>\n\u003Cp>Quand une page ne déclare aucune canonical, Google utilise les autres signaux pour en déduire une. L'absence de canonical auto-référente n'est pas une erreur en soi, mais elle laisse plus de latitude à Google pour choisir une URL que vous n'avez pas prévue — surtout si des paramètres de tracking (\u003Ccode>utm_source\u003C/code>, \u003Ccode>fbclid\u003C/code>) créent des variantes.\u003C/p>\n\u003Ch3>Scénario 8 : Pages paginées avec canonicals mal configurées\u003C/h3>\n\u003Cp>Les pages 2, 3, 4… d'une liste produit qui déclarent toutes une canonical vers la page 1. Google a explicitement dit que ce pattern est incorrect : chaque page paginée doit avoir une canonical auto-référente. Sinon, seule la page 1 sera indexée, et les produits des pages suivantes disparaissent de l'index.\u003C/p>\n\u003Ch3>Scénario 9 : Hreflang et canonical en conflit\u003C/h3>\n\u003Cp>Dans un \u003Ca href=\"/blog/seo-multilingue-architecture-technique-optimale\">contexte multilingue\u003C/a>, la page FR déclare une canonical vers elle-même ET un hreflang vers la page EN. Mais la page EN déclare une canonical vers la page FR. Google ne peut pas résoudre cette boucle. Le résultat : il ignore le hreflang et choisit la canonical qu'il estime la plus pertinente — souvent la version dans la langue la plus populaire pour la requête.\u003C/p>\n\u003Ch2>Diagnostic technique : détecter les conflits de canonical à grande échelle\u003C/h2>\n\u003Cp>Sur un site de 500 pages, un audit manuel suffit. Sur 15 000 pages, il faut automatiser. Voici un pipeline de diagnostic complet.\u003C/p>\n\u003Ch3>Étape 1 : Extraire les canonicals déclarées via crawl\u003C/h3>\n\u003Cp>Screaming Frog reste l'outil de référence pour l'extraction. Mais sur de gros volumes, un crawl programmatique avec Python est plus flexible :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> requests\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> bs4 \u003C/span>\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> BeautifulSoup\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> urllib.parse \u003C/span>\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> urlparse\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> csv\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">def\u003C/span>\u003Cspan style=\"color:#B392F0\"> audit_canonicals\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(urls: list[\u003C/span>\u003Cspan style=\"color:#79B8FF\">str\u003C/span>\u003Cspan style=\"color:#E1E4E8\">], output_file: \u003C/span>\u003Cspan style=\"color:#79B8FF\">str\u003C/span>\u003Cspan style=\"color:#E1E4E8\">):\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    \"\"\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    Pour chaque URL, vérifie la cohérence entre :\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    - l'URL crawlée\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    - la canonical HTML\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    - la canonical HTTP header\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    - le status code\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    \"\"\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> []\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> url \u003C/span>\u003Cspan style=\"color:#F97583\">in\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> urls:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        try\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            resp \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> requests.get(url, \u003C/span>\u003Cspan style=\"color:#FFAB70\">timeout\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">10\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">allow_redirects\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">True\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            final_url \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> resp.url  \u003C/span>\u003Cspan style=\"color:#6A737D\"># URL après redirections\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            status \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> resp.status_code\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">            # Canonical depuis le header HTTP\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            link_header \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> resp.headers.get(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Link\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            http_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\"> None\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">            if\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'rel=\"canonical\"'\u003C/span>\u003Cspan style=\"color:#F97583\"> in\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> link_header:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">                http_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> link_header.split(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\";\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)[\u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].strip(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"&#x3C;>\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">            # Canonical depuis le HTML\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            soup \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> BeautifulSoup(resp.text, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"html.parser\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            link_tag \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> soup.find(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"link\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, {\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"rel\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"canonical\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">})\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            html_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> link_tag[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"href\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">] \u003C/span>\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> link_tag \u003C/span>\u003Cspan style=\"color:#F97583\">else\u003C/span>\u003Cspan style=\"color:#79B8FF\"> None\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">            # Détection des conflits\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            conflict \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"NONE\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">            if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> html_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">and\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> http_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">and\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> html_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">!=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> http_canonical:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">                conflict \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"HTML_VS_HTTP_HEADER\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">            elif\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> html_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">and\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> html_canonical \u003C/span>\u003Cspan style=\"color:#F97583\">!=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> final_url:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">                conflict \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"CANONICAL_DIFFERS_FROM_URL\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">            elif\u003C/span>\u003Cspan style=\"color:#F97583\"> not\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> html_canonical:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">                conflict \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"MISSING_CANONICAL\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            results.append({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"requested_url\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: url,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"final_url\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: final_url,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"status\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: status,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"html_canonical\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: html_canonical,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"http_canonical\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: http_canonical,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"conflict\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: conflict,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            })\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        except\u003C/span>\u003Cspan style=\"color:#79B8FF\"> Exception\u003C/span>\u003Cspan style=\"color:#F97583\"> as\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> e:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            results.append({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"requested_url\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: url,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"final_url\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#79B8FF\">None\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"status\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"ERROR\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"html_canonical\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#79B8FF\">None\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"http_canonical\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#79B8FF\">None\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                \"conflict\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"FETCH_ERROR: \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">e\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            })\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    with\u003C/span>\u003Cspan style=\"color:#79B8FF\"> open\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(output_file, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"w\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">newline\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">as\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> f:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        writer \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> csv.DictWriter(f, \u003C/span>\u003Cspan style=\"color:#FFAB70\">fieldnames\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">results[\u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].keys())\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        writer.writeheader()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        writer.writerows(results)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    conflicts \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [r \u003C/span>\u003Cspan style=\"color:#F97583\">for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> r \u003C/span>\u003Cspan style=\"color:#F97583\">in\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> results \u003C/span>\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> r[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"conflict\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">] \u003C/span>\u003Cspan style=\"color:#F97583\">!=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"NONE\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Audit terminé : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{len\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(conflicts)\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> conflits sur \u003C/span>\u003Cspan style=\"color:#79B8FF\">{len\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(results)\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> URLs\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Usage avec les URLs extraites du sitemap\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> xml.etree \u003C/span>\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ElementTree\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">sitemap_resp \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> requests.get(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"https://shop.example.fr/sitemap.xml\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">tree \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ElementTree.fromstring(sitemap_resp.content)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">ns \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"s\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"http://www.sitemaps.org/schemas/sitemap/0.9\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">urls \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [loc.text \u003C/span>\u003Cspan style=\"color:#F97583\">for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> loc \u003C/span>\u003Cspan style=\"color:#F97583\">in\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> tree.findall(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\".//s:loc\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, ns)]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">audit_canonicals(urls, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"canonical_audit.csv\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Ce script détecte les quatre cas problématiques : canonical manquante, canonical différente de l'URL servie, conflit entre canonical HTML et header HTTP, erreur de fetch.\u003C/p>\n\u003Ch3>Étape 2 : Croiser avec Google Search Console\u003C/h3>\n\u003Cp>L'onglet \"Indexation des pages\" dans Google Search Console montre les URL que Google a choisies comme canoniques. Exportez ce rapport et croisez-le avec votre audit :\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>URL déclarée canonical = URL indexée par Google\u003C/strong> → OK\u003C/li>\n\u003Cli>\u003Cstrong>URL déclarée canonical ≠ URL indexée par Google\u003C/strong> → Google a ignoré votre canonical\u003C/li>\n\u003Cli>\u003Cstrong>URL non indexée, raison \"Autre page canonical\"\u003C/strong> → Google a élu une autre URL\u003C/li>\n\u003C/ul>\n\u003Cp>Ce croisement révèle les vrais désaccords entre votre intention et le choix de Google. Sur un site e-commerce de 15 000 pages, il n'est pas rare de trouver 8 à 15% d'URLs où Google a choisi une canonical différente de celle déclarée.\u003C/p>\n\u003Ch3>Étape 3 : Monitoring continu\u003C/h3>\n\u003Cp>L'audit ponctuel ne suffit pas. Une mise à jour de template, un déploiement qui casse le SSR, un \u003Ca href=\"/blog/changer-de-framework-next-js-vers-nuxt-ou-l-inverse-sans-perte-seo\">changement de framework\u003C/a> — tout cela peut introduire des régressions sur les canonicals du jour au lendemain. Un outil de monitoring comme Seogard détecte ces régressions en temps réel : si une canonical disparaît ou change sur un batch de pages, vous recevez une alerte avant que Google ne recrawle.\u003C/p>\n\u003Ch2>Scénario réel : migration SSR et canonicals cassées\u003C/h2>\n\u003Cp>Un média en ligne de 12 000 articles migre d'un \u003Ca href=\"/blog/headless-cms-et-seo-avantages-et-risques-techniques\">headless CMS\u003C/a> avec rendu côté client (CSR) vers un setup Next.js avec SSR. Avant la migration, les canonicals étaient injectées côté client via JavaScript. Googlebot les rendait correctement — le \u003Ca href=\"/blog/rendering-budget-de-google-combien-de-javascript-est-trop\">rendering budget\u003C/a> étant suffisant pour ce volume.\u003C/p>\n\u003Cp>Après la migration vers SSR, l'équipe dev a oublié de porter le composant \u003Ccode>&#x3C;Head>\u003C/code> qui injectait la balise canonical. Résultat : pendant 11 jours, les 12 000 pages ont été servies sans aucune canonical déclarée.\u003C/p>\n\u003Ch3>L'impact observé\u003C/h3>\n\u003Cul>\n\u003Cli>\u003Cstrong>Jours 1-3\u003C/strong> : rien de visible. Google n'a pas encore recrawlé massivement.\u003C/li>\n\u003Cli>\u003Cstrong>Jours 4-7\u003C/strong> : le crawl rate augmente (Google détecte le changement de stack et recrawle plus agressivement). Les pages sont indexées sans canonical.\u003C/li>\n\u003Cli>\u003Cstrong>Jours 8-11\u003C/strong> : Google commence à sélectionner ses propres canonicals. Pour 1 400 articles, il choisit des URL avec paramètres de tracking (\u003Ccode>?utm_source=newsletter\u003C/code>) qui avaient été crawlées via des liens dans des emails indexés par des webmails publics.\u003C/li>\n\u003Cli>\u003Cstrong>Impact trafic\u003C/strong> : -23% de trafic organique sur les 1 400 articles affectés, car Google a dilué les signaux entre la \"vraie\" URL et la variante UTM.\u003C/li>\n\u003C/ul>\n\u003Ch3>La correction\u003C/h3>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// components/SEOHead.tsx — Next.js App Router\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> { headers } \u003C/span>\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'next/headers'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">export\u003C/span>\u003Cspan style=\"color:#F97583\"> default\u003C/span>\u003Cspan style=\"color:#F97583\"> function\u003C/span>\u003Cspan style=\"color:#B392F0\"> SEOHead\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({ \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  title\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  description\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  canonicalPath\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> \u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  title\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  description\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  canonicalPath\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // Construire la canonical à partir du path, jamais depuis l'URL courante\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // pour éviter d'inclure des paramètres de tracking\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> baseUrl\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'https://media.example.fr'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> canonical\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> `${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">baseUrl\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">canonicalPath\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    &#x3C;>\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      &#x3C;\u003C/span>\u003Cspan style=\"color:#B392F0\">title\u003C/span>\u003Cspan style=\"color:#E1E4E8\">>{title}\u003C/span>\u003Cspan style=\"color:#F97583\">&#x3C;/\u003C/span>\u003Cspan style=\"color:#E1E4E8\">title\u003C/span>\u003Cspan style=\"color:#F97583\">>\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      &#x3C;\u003C/span>\u003Cspan style=\"color:#E1E4E8\">meta name\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"description\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> content\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">{description} \u003C/span>\u003Cspan style=\"color:#F97583\">/>\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      &#x3C;\u003C/span>\u003Cspan style=\"color:#E1E4E8\">link rel\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"canonical\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> href\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">{canonical} \u003C/span>\u003Cspan style=\"color:#F97583\">/>\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    &#x3C;/>\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  );\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Le point clé : la canonical est construite à partir d'un \u003Ccode>canonicalPath\u003C/code> stocké en base de données, jamais dérivée de l'URL de la requête HTTP entrante. C'est la seule façon d'éviter que des paramètres de query string se glissent dans la canonical.\u003C/p>\n\u003Cp>Après correction et soumission d'un recrawl via Search Console (Inspection d'URL > Demander l'indexation pour les pages critiques, puis un nouveau sitemap ping), le trafic est revenu à son niveau initial en 18 jours.\u003C/p>\n\u003Ch2>Les signaux que Google pondère le plus (par ordre d'importance observé)\u003C/h2>\n\u003Cp>Google n'a jamais publié de hiérarchie officielle des signaux de canonicalisation. Mais en croisant les déclarations de Mueller, la documentation officielle, et les observations à grande échelle sur des centaines de sites, un ordre de priorité se dessine :\u003C/p>\n\u003Col>\n\u003Cli>\u003Cstrong>Redirections 301\u003C/strong> — le signal le plus fort. Une 301 est quasi-toujours respectée comme signal canonical.\u003C/li>\n\u003Cli>\u003Cstrong>Canonical HTML + header HTTP cohérents\u003C/strong> — quand les deux sont présents et concordants, Google les suit dans la grande majorité des cas.\u003C/li>\n\u003Cli>\u003Cstrong>URL dans le sitemap XML\u003C/strong> — poids modéré, mais c'est un départageur quand les autres signaux sont ambigus.\u003C/li>\n\u003Cli>\u003Cstrong>Liens internes\u003C/strong> — la variante qui reçoit le plus de maillage interne est favorisée. C'est un signal souvent sous-estimé.\u003C/li>\n\u003Cli>\u003Cstrong>HTTPS > HTTP\u003C/strong> — préférence systématique sauf si tous les autres signaux pointent vers HTTP.\u003C/li>\n\u003Cli>\u003Cstrong>Liens externes\u003C/strong> — si une variante a significativement plus de backlinks, elle peut l'emporter.\u003C/li>\n\u003C/ol>\n\u003Ch3>Le piège des redirections 302\u003C/h3>\n\u003Cp>Les 302 (redirect temporaire) sont un cas ambivalent. Google a confirmé que les 302 transmettent les signaux de ranking comme les 301 dans la plupart des cas. Mais pour la canonicalisation spécifiquement, une 302 longue durée (plus de quelques semaines) sera traitée comme une 301 de facto. Le risque : pendant la période d'ambiguïté, Google peut hésiter entre les deux URL.\u003C/p>\n\u003Cp>Règle simple : si la redirection est permanente, utilisez 301. Toujours. Les \u003Ca href=\"/blog/a-b-testing-seo-tester-sans-penaliser-le-referencement\">tests A/B\u003C/a> sont le seul cas légitime de 302 durable.\u003C/p>\n\u003Ch2>Edge cases et trade-offs à connaître\u003C/h2>\n\u003Ch3>Pages avec contenu rendu en JavaScript\u003C/h3>\n\u003Cp>Si votre canonical est injectée par JavaScript et que Googlebot ne rend pas la page (timeout, erreur JS, dépassement du rendering budget), la canonical n'existe pas pour Google. C'est le scénario 7 (canonical absente) par défaut.\u003C/p>\n\u003Cp>Vérifiez systématiquement le rendu Googlebot via le test d'URL enrichie de Search Console ou via Chrome DevTools en désactivant JavaScript :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Vérifier ce que Googlebot voit sans JS rendering\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">curl\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -s\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -A\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  https://shop.example.fr/produit/chaussure-nike-air\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  |\u003C/span>\u003Cspan style=\"color:#B392F0\"> grep\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -i\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"canonical\"\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Si cette commande ne retourne rien, votre canonical dépend du rendering JavaScript. C'est un risque si vous êtes sur une stack \u003Ca href=\"/blog/api-first-et-seo-servir-du-contenu-crawlable-depuis-une-api\">API-first\u003C/a> sans SSR.\u003C/p>\n\u003Ch3>Canonicals cross-domain\u003C/h3>\n\u003Cp>Vous pouvez déclarer une canonical vers un autre domaine (syndication de contenu, par exemple). Google respecte ce signal, mais avec plus de prudence. Le domaine cible doit être accessible, retourner un 200, et le contenu doit être suffisamment similaire. Si le domaine cible bloque Googlebot ou retourne un contenu différent, Google ignorera la canonical cross-domain.\u003C/p>\n\u003Ch3>Canonical et pages indexées via Google Discover\u003C/h3>\n\u003Cp>Pour les sites médias qui reçoivent du trafic \u003Ca href=\"/blog/inside-google-discover-20-pipelines-42-million-cards-and-what-they-mean-for-publishers\">Google Discover\u003C/a>, la canonical est encore plus critique. Discover utilise l'URL canonique pour décider quelle version afficher dans le feed. Une canonical mal configurée peut rediriger le trafic Discover vers une URL qui n'est pas celle optimisée pour l'engagement mobile.\u003C/p>\n\u003Ch2>Checklist opérationnelle post-Mueller\u003C/h2>\n\u003Cp>Voici les vérifications à implémenter immédiatement après lecture de ces 9 scénarios :\u003C/p>\n\u003Cp>\u003Cstrong>Au niveau du template :\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>Chaque page a une canonical auto-référente, sauf si elle est explicitement un doublon.\u003C/li>\n\u003Cli>La canonical est dans le HTML initial (pas injectée par JS uniquement).\u003C/li>\n\u003Cli>La canonical utilise le même protocole (HTTPS) et le même sous-domaine (www ou non) que l'URL servie.\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Au niveau du serveur :\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>Les redirections 301 et les canonicals pointent vers la même URL finale.\u003C/li>\n\u003Cli>Le header HTTP \u003Ccode>Link: rel=canonical\u003C/code> est cohérent avec la balise HTML (ou absent — les deux ne sont pas nécessaires simultanément).\u003C/li>\n\u003Cli>Les paramètres de tracking sont exclus des canonicals.\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Au niveau du sitemap :\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>Seules les URL canoniques sont dans le sitemap. Jamais de variantes paramétrées ou de doublons.\u003C/li>\n\u003Cli>Le sitemap est à jour après chaque \u003Ca href=\"/blog/google-confirms-march-2026-core-update-is-complete-via-sejournal-mattgsouthern\">mise à jour majeure\u003C/a> ou refonte.\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Au niveau du monitoring :\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>Croisement régulier (hebdomadaire minimum) entre les canonicals déclarées et celles choisies par Google dans Search Console.\u003C/li>\n\u003Cli>Alertes automatiques si une canonical disparaît ou change sur plus de 1% des pages.\u003C/li>\n\u003C/ul>\n\u003Cp>La canonicalisation n'est pas un sujet qu'on configure une fois et qu'on oublie. Chaque déploiement, chaque migration, chaque changement de CDN peut introduire une régression silencieuse. Les 9 scénarios de Mueller ne sont pas des cas théoriques — ce sont des bugs que Seogard détecte quotidiennement sur les sites qu'il monitore. La seule protection fiable, c'est un monitoring continu qui compare l'intention (votre canonical déclarée) avec la réalité (ce que Google a effectivement indexé).\u003C/p>\n\u003Cpre>\u003Ccode>\u003C/code>\u003C/pre>",null,12,[18,19,20,21,22],"canonical","google","indexation","duplicate-content","seo-technique","Canonicalisation Google : les 9 scénarios décryptés","Tue Apr 14 2026 15:02:37 GMT+0000 (Coordinated Universal Time)",[26,40,55],{"_id":27,"slug":28,"__v":6,"author":7,"canonical":29,"category":10,"createdAt":30,"date":12,"description":31,"image":15,"imageAlt":15,"readingTime":16,"tags":32,"title":38,"updatedAt":39},"69de8142aa6b273b0cdacf99","google-s-task-based-agentic-search-is-disrupting-seo-today-not-tomorrow-via-sejournal-martinibuster","https://seogard.io/blog/google-s-task-based-agentic-search-is-disrupting-seo-today-not-tomorrow-via-sejournal-martinibuster","2026-04-14T18:02:42.687Z","Google passe à la recherche agentique par tâches. Analyse technique des impacts sur le crawl, le rendering et l'architecture SEO de vos sites.",[33,34,35,36,37],"agentic search","google AI","SEO technique","crawl budget","structured data","Agentic Search Google : impact SEO technique concret","Tue Apr 14 2026 18:02:42 GMT+0000 (Coordinated Universal Time)",{"_id":41,"slug":42,"__v":6,"author":7,"canonical":43,"category":10,"createdAt":44,"date":45,"description":46,"image":15,"imageAlt":15,"readingTime":47,"tags":48,"title":53,"updatedAt":54},"69db3593aa6b273b0c387277","why-product-feeds-shouldn-t-be-the-most-ignored-seo-system-in-ecommerce","https://seogard.io/blog/why-product-feeds-shouldn-t-be-the-most-ignored-seo-system-in-ecommerce","2026-04-12T06:02:59.904Z","2026-04-12","Les product feeds pilotent Google Shopping, structured data et AI search. Voici comment les transformer en levier SEO technique majeur.",14,[49,50,37,51,52],"product feeds","ecommerce SEO","Google Merchant Center","AI search","Product Feeds & SEO : le système le plus négligé du e-commerce","Sun Apr 12 2026 06:02:59 GMT+0000 (Coordinated Universal Time)",{"_id":56,"slug":57,"__v":6,"author":7,"canonical":58,"category":10,"createdAt":59,"date":45,"description":60,"image":15,"imageAlt":15,"readingTime":16,"tags":61,"title":65,"updatedAt":66},"69db6dc1aa6b273b0c6553f6","google-says-it-can-handle-multiple-urls-to-the-same-content-via-sejournal-martinibuster","https://seogard.io/blog/google-says-it-can-handle-multiple-urls-to-the-same-content-via-sejournal-martinibuster","2026-04-12T10:02:41.445Z","Google affirme gérer les URLs multiples pointant vers un même contenu. Analyse technique des mécanismes réels, limites et configurations à maîtriser.",[62,18,36,63,64],"duplicate content","google indexation","urls multiples","Duplicate content et URLs multiples : ce que Google gère vraiment","Sun Apr 12 2026 10:02:41 GMT+0000 (Coordinated Universal Time)"]