[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fJX-pY8VLwURjxoDXWox_VE1GnxlX4V-x5xlbe1SiOVM":3,"$fRrOqQBz3hpRyUZJDf6cqP-FP4lZlvyo7E8etcUjTZ-0":23,"$fcTFjTaPPT1sVcnh1wWEGoTWMSaOD4bwmwntkMae6w5s":112},{"_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":21,"updatedAt":22},"6a378c35aa6b273b0cab61da","detecter-noindex-production-avant-google-ci-cd",0,"Equipe Seogard","Un `noindex` ajouté par erreur en production ne casse rien de visible. Le site répond 200, les pages s'affichent, les tests fonctionnels passent. Puis le trafic organique s'effondre sur deux à trois semaines. Le problème : quand vous le voyez dans la Search Console, la désindexation est déjà avancée. Lors du développement ou de la migration d'un site, une balise meta robots noindex est souvent ajoutée pour empêcher l'indexation de l'environnement de développement. Si cette balise n'est pas retirée avant la mise en production, l'intégralité du site peut disparaître de Google en quelques semaines.\n\nLa réponse n'est pas un audit mensuel ni une alerte GSC. C'est un contrôle à chaque déploiement, avant que Googlebot ne recrawle. Cet article explique comment détecter un `noindex` régressif sur les deux surfaces qui comptent — le HTML brut SSR et le rendu JS — et comment bloquer le deploy en CI avant que la régression n'atteigne la prod.\n\n## Pourquoi la Search Console arrive toujours trop tard\n\nGSC est un outil de constat, pas de prévention. Sous Disponibilité > Indexation > Indexation autorisée ?, vérifiez si l'instruction \"noindex\" est toujours détectée. Si elle n'est plus présente, vous pouvez cliquer sur Demander l'indexation. Si l'instruction \"noindex\" existe toujours, vous devez la supprimer pour que la page soit indexée. Le problème : ce signal n'apparaît qu'**après** que Googlebot a recrawlé l'URL et appliqué la directive. Vous découvrez la régression au moment où elle produit déjà ses effets.\n\nLes guides SEO classiques recommandent un crawl Screaming Frog après mise en ligne, ou une surveillance manuelle du rapport d'indexation. Deux limites. D'abord, c'est réactif : vous crawlez après le deploy, pas avant. Ensuite, un crawl manuel ne tourne pas à chaque merge. Entre deux audits, vous avez le temps de pousser dix déploiements régressifs.\n\nL'enjeu réel est de déplacer le contrôle **avant** la production, dans le pipeline. C'est un problème d'observabilité de déploiement, pas un problème d'audit SEO.\n\n## Les deux surfaces où le noindex se cache\n\nUn `noindex` régressif n'apparaît pas toujours là où vous le cherchez. Il faut surveiller trois vecteurs distincts.\n\n### 1. La meta robots dans le HTML brut (SSR)\n\nLe cas le plus courant : la balise héritée du staging.\n\n```html\n\u003C!-- ❌ régressif : hérité de l'env de préprod -->\n\u003Cmeta name=\"robots\" content=\"noindex, nofollow\">\n\n\u003C!-- ✅ attendu en production -->\n\u003Cmeta name=\"robots\" content=\"index, follow\">\n```\n\nSur un site SSR ou SSG, cette balise est dans le HTML servi par le serveur. Un `curl` la voit. Le piège classique reste celui de la préproduction : le site de préproduction est volontairement mis en noindex, puis cette directive reste en place après la mise en production. Résultat : le nouveau site est en ligne, mais ses pages stratégiques demandent à Google de ne pas les indexer.\n\n### 2. Le X-Robots-Tag dans l'en-tête HTTP (invisible dans le source)\n\nLe vecteur le plus traître. Une directive X-Robots-Tag: noindex envoyée par le serveur ne se voit pas dans le code source de la page. Elle suffit pourtant à exclure une URL de l'index. Vous pouvez ouvrir le code source, faire Ctrl+F sur \"noindex\", ne rien trouver — et avoir quand même un `noindex` actif. Noindex peut aussi être envoyé via un en-tête de réponse HTTP. C'est courant, et il peut être introduit par des règles serveur ou un CDN.\n\nUne règle Nginx, une config Cloudflare, une variable d'environnement mal propagée : tous peuvent injecter cet en-tête sans laisser de trace dans le HTML. Votre contrôle doit donc inspecter les **response headers**, pas seulement le DOM.\n\n### 3. Le noindex injecté côté JS (CSR)\n\nLe piège des SPA. Le HTML brut est propre. Mais un script modifie le DOM au runtime. Google tient compte du code final obtenu après interprétation du Javascript au chargement de la page. Donc si un script JS modifie dynamiquement le HTML initial pour ajouter \"noindex\", alors Google considèrera que la page ne doit pas/plus être indexée.\n\nIci, un `curl` ne suffit plus. Le HTML brut affiche `index, follow`, mais après hydratation un composant — un guard de route, une logique de feature flag, un état d'auth mal géré — réécrit la meta en `noindex`. Googlebot exécute le JS et voit la version finale. Vous, en testant avec `curl`, voyez la version saine. C'est exactement le type de divergence SSR/CSR qui passe sous tous les radars. Le même mécanisme produit des [SPA non indexées que Googlebot voit en page blanche](/blog/spa-non-indexee-google-page-blanche-diagnostic).\n\n**Conclusion technique : un seul check ne couvre jamais les trois.** Il faut comparer le HTML brut (réponse serveur + headers) ET le DOM rendu après exécution JS, sur chaque déploiement.\n\n## Détecter avant Googlebot : le contrôle à intégrer\n\nL'objectif est de transformer \"le noindex est-il toujours là ?\" en assertion automatisée, exécutée avant que la prod ne soit live.\n\n### Vérification manuelle minimale (à scripter ensuite)\n\nInspectez les deux surfaces d'un coup. Pour le HTML brut et les headers :\n\n```bash\n# Meta robots dans le HTML brut + X-Robots-Tag dans les headers\ncurl -sI https://votresite.com/page-strategique | grep -i \"x-robots-tag\"\ncurl -s  https://votresite.com/page-strategique | grep -i 'name=\"robots\"'\n```\n\nPour le rendu JS, un check via Playwright qui lit le DOM après hydratation :\n\n```js\nimport { chromium } from 'playwright';\n\nconst ROUTES = ['/', '/produits', '/produits/ref-cle'];\n\nfor (const route of ROUTES) {\n  const browser = await chromium.launch();\n  const page = await browser.newPage();\n  const resp = await page.goto(`https://votresite.com${route}`, {\n    waitUntil: 'networkidle',\n  });\n\n  // 1. en-tête HTTP\n  const xrobots = resp.headers()['x-robots-tag'] ?? '';\n  // 2. meta robots dans le DOM rendu (post-JS)\n  const meta = await page.getAttribute('meta[name=\"robots\"]', 'content') ?? '';\n\n  if (/noindex/i.test(xrobots) || /noindex/i.test(meta)) {\n    console.error(`REGRESSION noindex sur ${route} — header=\"${xrobots}\" meta=\"${meta}\"`);\n    process.exit(1);\n  }\n  await browser.close();\n}\n```\n\nCe script échoue avec un code de sortie non nul dès qu'un `noindex` apparaît, sur l'une des deux surfaces. C'est la brique de base d'un gate CI.\n\n### Le piège du robots.txt qui masque le noindex\n\nAttention à un effet de bord documenté par Google. Si la page est bloquée via le fichier robots.txt, la valeur \"Indexation autorisée\" indique toujours \"Oui\", car la directive noindex ne peut pas être identifiée ni appliquée par Google. C'est pourquoi votre page peut apparaître dans les résultats de recherche. Si votre check repose sur GSC, un `Disallow` peut masquer un `noindex` réel. Votre contrôle CI, lui, lit directement la réponse serveur — il n'est pas trompé par ce masquage. Raison de plus pour ne pas dépendre de la Search Console.\n\n## Bloquer le déploiement régressif dans la CI/CD\n\nLe check ne sert à rien s'il ne bloque pas. L'intégration dans le pipeline transforme la détection en garde-fou.\n\nLe principe d'une [gate SEO dans GitHub Actions qui fait échouer un déploiement régressif](/blog/gate-seo-github-actions-echouer-deploiement-regressif) : le job tourne contre l'URL de preview (ou la staging avec headers de prod simulés) **avant** la promotion vers la production. Si une route stratégique renvoie `noindex` sur le HTML brut, dans le `X-Robots-Tag`, ou après rendu JS, le job échoue et la promotion est annulée.\n\nC'est là qu'un monitoring de régression dédié intervient. Sur Seogard, la règle `noindex_added` compare l'état d'indexabilité de chaque URL surveillée entre le déploiement précédent et le nouveau, sur le HTML brut SSR **et** le DOM rendu après exécution JS. Quand une page passe de `index` à `noindex` — meta ou X-Robots-Tag — la règle se déclenche avant le recrawl Google et peut bloquer le deploy via le hook CI. La même logique couvre `robots_txt_changed` pour le cas où un `Disallow: /` apparaît dans le `robots.txt`, l'autre vecteur classique de désindexation massive en migration. Vous détectez la régression au moment du push, pas trois semaines plus tard dans GSC. Voir [comment Seogard surveille les régressions d'indexation](/).\n\n## Checklist de déploiement\n\nÀ automatiser dans le pipeline, pas à faire à la main :\n\n1. **HTML brut** : `meta robots` absente ou en `index` sur toutes les routes stratégiques.\n2. **Headers HTTP** : pas de `X-Robots-Tag: noindex` côté serveur ou CDN.\n3. **Rendu JS** : le DOM post-hydratation ne réécrit pas la meta en `noindex`.\n4. **robots.txt** : pas de `Disallow: /` global, et la directive ne masque pas un `noindex` que vous croyez actif.\n5. **Gate CI** : le job échoue et annule la promotion si l'un des points ci-dessus régresse.\n\nLa règle est simple : ne faites jamais confiance à un environnement qui hérite de la config d'un autre. Un `noindex` est un commit comme un autre — il doit être testé avant la prod, sur les deux surfaces, à chaque déploiement.\n\nLa référence officielle sur le comportement de la directive : [Block Search indexing with noindex — Google Search Central](https://developers.google.com/search/docs/crawling-indexing/block-indexing).\n```","https://seogard.io/blog/detecter-noindex-production-avant-google-ci-cd","Régressions SEO","2026-06-21T07:01:09.618Z","2026-06-21","Un noindex oublié en prod désindexe votre site en quelques semaines. Voici comment le détecter avant Googlebot, dans la CI/CD, HTML brut et rendu JS.","\u003Cp>Un \u003Ccode>noindex\u003C/code> ajouté par erreur en production ne casse rien de visible. Le site répond 200, les pages s'affichent, les tests fonctionnels passent. Puis le trafic organique s'effondre sur deux à trois semaines. Le problème : quand vous le voyez dans la Search Console, la désindexation est déjà avancée. Lors du développement ou de la migration d'un site, une balise meta robots noindex est souvent ajoutée pour empêcher l'indexation de l'environnement de développement. Si cette balise n'est pas retirée avant la mise en production, l'intégralité du site peut disparaître de Google en quelques semaines.\u003C/p>\n\u003Cp>La réponse n'est pas un audit mensuel ni une alerte GSC. C'est un contrôle à chaque déploiement, avant que Googlebot ne recrawle. Cet article explique comment détecter un \u003Ccode>noindex\u003C/code> régressif sur les deux surfaces qui comptent — le HTML brut SSR et le rendu JS — et comment bloquer le deploy en CI avant que la régression n'atteigne la prod.\u003C/p>\n\u003Ch2>Pourquoi la Search Console arrive toujours trop tard\u003C/h2>\n\u003Cp>GSC est un outil de constat, pas de prévention. Sous Disponibilité > Indexation > Indexation autorisée ?, vérifiez si l'instruction \"noindex\" est toujours détectée. Si elle n'est plus présente, vous pouvez cliquer sur Demander l'indexation. Si l'instruction \"noindex\" existe toujours, vous devez la supprimer pour que la page soit indexée. Le problème : ce signal n'apparaît qu'\u003Cstrong>après\u003C/strong> que Googlebot a recrawlé l'URL et appliqué la directive. Vous découvrez la régression au moment où elle produit déjà ses effets.\u003C/p>\n\u003Cp>Les guides SEO classiques recommandent un crawl Screaming Frog après mise en ligne, ou une surveillance manuelle du rapport d'indexation. Deux limites. D'abord, c'est réactif : vous crawlez après le deploy, pas avant. Ensuite, un crawl manuel ne tourne pas à chaque merge. Entre deux audits, vous avez le temps de pousser dix déploiements régressifs.\u003C/p>\n\u003Cp>L'enjeu réel est de déplacer le contrôle \u003Cstrong>avant\u003C/strong> la production, dans le pipeline. C'est un problème d'observabilité de déploiement, pas un problème d'audit SEO.\u003C/p>\n\u003Ch2>Les deux surfaces où le noindex se cache\u003C/h2>\n\u003Cp>Un \u003Ccode>noindex\u003C/code> régressif n'apparaît pas toujours là où vous le cherchez. Il faut surveiller trois vecteurs distincts.\u003C/p>\n\u003Ch3>1. La meta robots dans le HTML brut (SSR)\u003C/h3>\n\u003Cp>Le cas le plus courant : la balise héritée du staging.\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;!-- ❌ régressif : hérité de l'env de préprod -->\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"color:#85E89D\">meta\u003C/span>\u003Cspan style=\"color:#B392F0\"> name\u003C/span>\u003Cspan style=\"color:#E1E4E8\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"robots\"\u003C/span>\u003Cspan style=\"color:#B392F0\"> content\u003C/span>\u003Cspan style=\"color:#E1E4E8\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"noindex, nofollow\"\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;!-- ✅ attendu en production -->\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"color:#85E89D\">meta\u003C/span>\u003Cspan style=\"color:#B392F0\"> name\u003C/span>\u003Cspan style=\"color:#E1E4E8\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"robots\"\u003C/span>\u003Cspan style=\"color:#B392F0\"> content\u003C/span>\u003Cspan style=\"color:#E1E4E8\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"index, follow\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">>\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Sur un site SSR ou SSG, cette balise est dans le HTML servi par le serveur. Un \u003Ccode>curl\u003C/code> la voit. Le piège classique reste celui de la préproduction : le site de préproduction est volontairement mis en noindex, puis cette directive reste en place après la mise en production. Résultat : le nouveau site est en ligne, mais ses pages stratégiques demandent à Google de ne pas les indexer.\u003C/p>\n\u003Ch3>2. Le X-Robots-Tag dans l'en-tête HTTP (invisible dans le source)\u003C/h3>\n\u003Cp>Le vecteur le plus traître. Une directive X-Robots-Tag: noindex envoyée par le serveur ne se voit pas dans le code source de la page. Elle suffit pourtant à exclure une URL de l'index. Vous pouvez ouvrir le code source, faire Ctrl+F sur \"noindex\", ne rien trouver — et avoir quand même un \u003Ccode>noindex\u003C/code> actif. Noindex peut aussi être envoyé via un en-tête de réponse HTTP. C'est courant, et il peut être introduit par des règles serveur ou un CDN.\u003C/p>\n\u003Cp>Une règle Nginx, une config Cloudflare, une variable d'environnement mal propagée : tous peuvent injecter cet en-tête sans laisser de trace dans le HTML. Votre contrôle doit donc inspecter les \u003Cstrong>response headers\u003C/strong>, pas seulement le DOM.\u003C/p>\n\u003Ch3>3. Le noindex injecté côté JS (CSR)\u003C/h3>\n\u003Cp>Le piège des SPA. Le HTML brut est propre. Mais un script modifie le DOM au runtime. Google tient compte du code final obtenu après interprétation du Javascript au chargement de la page. Donc si un script JS modifie dynamiquement le HTML initial pour ajouter \"noindex\", alors Google considèrera que la page ne doit pas/plus être indexée.\u003C/p>\n\u003Cp>Ici, un \u003Ccode>curl\u003C/code> ne suffit plus. Le HTML brut affiche \u003Ccode>index, follow\u003C/code>, mais après hydratation un composant — un guard de route, une logique de feature flag, un état d'auth mal géré — réécrit la meta en \u003Ccode>noindex\u003C/code>. Googlebot exécute le JS et voit la version finale. Vous, en testant avec \u003Ccode>curl\u003C/code>, voyez la version saine. C'est exactement le type de divergence SSR/CSR qui passe sous tous les radars. Le même mécanisme produit des \u003Ca href=\"/blog/spa-non-indexee-google-page-blanche-diagnostic\">SPA non indexées que Googlebot voit en page blanche\u003C/a>.\u003C/p>\n\u003Cp>\u003Cstrong>Conclusion technique : un seul check ne couvre jamais les trois.\u003C/strong> Il faut comparer le HTML brut (réponse serveur + headers) ET le DOM rendu après exécution JS, sur chaque déploiement.\u003C/p>\n\u003Ch2>Détecter avant Googlebot : le contrôle à intégrer\u003C/h2>\n\u003Cp>L'objectif est de transformer \"le noindex est-il toujours là ?\" en assertion automatisée, exécutée avant que la prod ne soit live.\u003C/p>\n\u003Ch3>Vérification manuelle minimale (à scripter ensuite)\u003C/h3>\n\u003Cp>Inspectez les deux surfaces d'un coup. Pour le HTML brut et les headers :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Meta robots dans le HTML brut + X-Robots-Tag dans les headers\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">curl\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -sI\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> https://votresite.com/page-strategique\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> grep\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -i\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"x-robots-tag\"\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:#9ECBFF\">  https://votresite.com/page-strategique\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> grep\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -i\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'name=\"robots\"'\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Pour le rendu JS, un check via Playwright qui lit le DOM après hydratation :\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\"> { chromium } \u003C/span>\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'playwright'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> ROUTES\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'/'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'/produits'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'/produits/ref-cle'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"color:#F97583\">const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> route\u003C/span>\u003Cspan style=\"color:#F97583\"> of\u003C/span>\u003Cspan style=\"color:#79B8FF\"> ROUTES\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\"> browser\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> chromium.\u003C/span>\u003Cspan style=\"color:#B392F0\">launch\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\"> page\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> browser.\u003C/span>\u003Cspan style=\"color:#B392F0\">newPage\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\"> resp\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> page.\u003C/span>\u003Cspan style=\"color:#B392F0\">goto\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`https://votresite.com${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">route\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    waitUntil: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'networkidle'\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\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // 1. en-tête HTTP\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> xrobots\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> resp.\u003C/span>\u003Cspan style=\"color:#B392F0\">headers\u003C/span>\u003Cspan style=\"color:#E1E4E8\">()[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'x-robots-tag'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">] \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:#6A737D\">  // 2. meta robots dans le DOM rendu (post-JS)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> meta\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> page.\u003C/span>\u003Cspan style=\"color:#B392F0\">getAttribute\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'meta[name=\"robots\"]'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'content'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \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\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"color:#9ECBFF\">/\u003C/span>\u003Cspan style=\"color:#DBEDFF\">noindex\u003C/span>\u003Cspan style=\"color:#9ECBFF\">/\u003C/span>\u003Cspan style=\"color:#F97583\">i\u003C/span>\u003Cspan style=\"color:#E1E4E8\">.\u003C/span>\u003Cspan style=\"color:#B392F0\">test\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(xrobots) \u003C/span>\u003Cspan style=\"color:#F97583\">||\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /\u003C/span>\u003Cspan style=\"color:#DBEDFF\">noindex\u003C/span>\u003Cspan style=\"color:#9ECBFF\">/\u003C/span>\u003Cspan style=\"color:#F97583\">i\u003C/span>\u003Cspan style=\"color:#E1E4E8\">.\u003C/span>\u003Cspan style=\"color:#B392F0\">test\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(meta)) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">error\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`REGRESSION noindex sur ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">route\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} — header=\"${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">xrobots\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}\" meta=\"${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">meta\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}\"`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    process.\u003C/span>\u003Cspan style=\"color:#B392F0\">exit\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">1\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\">  await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> browser.\u003C/span>\u003Cspan style=\"color:#B392F0\">close\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\u003Cp>Ce script échoue avec un code de sortie non nul dès qu'un \u003Ccode>noindex\u003C/code> apparaît, sur l'une des deux surfaces. C'est la brique de base d'un gate CI.\u003C/p>\n\u003Ch3>Le piège du robots.txt qui masque le noindex\u003C/h3>\n\u003Cp>Attention à un effet de bord documenté par Google. Si la page est bloquée via le fichier robots.txt, la valeur \"Indexation autorisée\" indique toujours \"Oui\", car la directive noindex ne peut pas être identifiée ni appliquée par Google. C'est pourquoi votre page peut apparaître dans les résultats de recherche. Si votre check repose sur GSC, un \u003Ccode>Disallow\u003C/code> peut masquer un \u003Ccode>noindex\u003C/code> réel. Votre contrôle CI, lui, lit directement la réponse serveur — il n'est pas trompé par ce masquage. Raison de plus pour ne pas dépendre de la Search Console.\u003C/p>\n\u003Ch2>Bloquer le déploiement régressif dans la CI/CD\u003C/h2>\n\u003Cp>Le check ne sert à rien s'il ne bloque pas. L'intégration dans le pipeline transforme la détection en garde-fou.\u003C/p>\n\u003Cp>Le principe d'une \u003Ca href=\"/blog/gate-seo-github-actions-echouer-deploiement-regressif\">gate SEO dans GitHub Actions qui fait échouer un déploiement régressif\u003C/a> : le job tourne contre l'URL de preview (ou la staging avec headers de prod simulés) \u003Cstrong>avant\u003C/strong> la promotion vers la production. Si une route stratégique renvoie \u003Ccode>noindex\u003C/code> sur le HTML brut, dans le \u003Ccode>X-Robots-Tag\u003C/code>, ou après rendu JS, le job échoue et la promotion est annulée.\u003C/p>\n\u003Cp>C'est là qu'un monitoring de régression dédié intervient. Sur Seogard, la règle \u003Ccode>noindex_added\u003C/code> compare l'état d'indexabilité de chaque URL surveillée entre le déploiement précédent et le nouveau, sur le HTML brut SSR \u003Cstrong>et\u003C/strong> le DOM rendu après exécution JS. Quand une page passe de \u003Ccode>index\u003C/code> à \u003Ccode>noindex\u003C/code> — meta ou X-Robots-Tag — la règle se déclenche avant le recrawl Google et peut bloquer le deploy via le hook CI. La même logique couvre \u003Ccode>robots_txt_changed\u003C/code> pour le cas où un \u003Ccode>Disallow: /\u003C/code> apparaît dans le \u003Ccode>robots.txt\u003C/code>, l'autre vecteur classique de désindexation massive en migration. Vous détectez la régression au moment du push, pas trois semaines plus tard dans GSC. Voir \u003Ca href=\"/\">comment Seogard surveille les régressions d'indexation\u003C/a>.\u003C/p>\n\u003Ch2>Checklist de déploiement\u003C/h2>\n\u003Cp>À automatiser dans le pipeline, pas à faire à la main :\u003C/p>\n\u003Col>\n\u003Cli>\u003Cstrong>HTML brut\u003C/strong> : \u003Ccode>meta robots\u003C/code> absente ou en \u003Ccode>index\u003C/code> sur toutes les routes stratégiques.\u003C/li>\n\u003Cli>\u003Cstrong>Headers HTTP\u003C/strong> : pas de \u003Ccode>X-Robots-Tag: noindex\u003C/code> côté serveur ou CDN.\u003C/li>\n\u003Cli>\u003Cstrong>Rendu JS\u003C/strong> : le DOM post-hydratation ne réécrit pas la meta en \u003Ccode>noindex\u003C/code>.\u003C/li>\n\u003Cli>\u003Cstrong>robots.txt\u003C/strong> : pas de \u003Ccode>Disallow: /\u003C/code> global, et la directive ne masque pas un \u003Ccode>noindex\u003C/code> que vous croyez actif.\u003C/li>\n\u003Cli>\u003Cstrong>Gate CI\u003C/strong> : le job échoue et annule la promotion si l'un des points ci-dessus régresse.\u003C/li>\n\u003C/ol>\n\u003Cp>La règle est simple : ne faites jamais confiance à un environnement qui hérite de la config d'un autre. Un \u003Ccode>noindex\u003C/code> est un commit comme un autre — il doit être testé avant la prod, sur les deux surfaces, à chaque déploiement.\u003C/p>\n\u003Cp>La référence officielle sur le comportement de la directive : \u003Ca href=\"https://developers.google.com/search/docs/crawling-indexing/block-indexing\">Block Search indexing with noindex — Google Search Central\u003C/a>.\u003C/p>\n\u003Cpre>\u003Ccode>\u003C/code>\u003C/pre>",null,9,[18,19,20],"noindex","ci-cd","regression-seo","Détecter un noindex en production avant Google (CI/CD)","Sun Jun 21 2026 07:01:09 GMT+0000 (Coordinated Universal Time)",[24,39,52,67,83,97],{"_id":25,"slug":26,"__v":6,"author":7,"canonical":27,"category":10,"createdAt":28,"date":29,"description":30,"image":15,"imageAlt":15,"readingTime":31,"tags":32,"title":37,"updatedAt":38,"categoryLegacy":34},"6a34db4aaa6b273b0c724ac7","hreflang-generated-pointent-vers-des-domaines-supprimes","https://seogard.io/blog/hreflang-generated-pointent-vers-des-domaines-supprimes","2026-06-19T06:01:46.165Z","2026-06-19","Un marché allemand fermé, des hreflang encore générés vers le .de mort. Récit de l'incident, diagnostic technique et fix complet.",12,[33,34,35,36],"hreflang","i18n","cleanup","domains","Hreflang vers domaines supprimés : −38% de trafic DE en 6 semaines","Fri Jun 19 2026 06:01:46 GMT+0000 (Coordinated Universal Time)",{"_id":40,"slug":41,"__v":6,"author":7,"canonical":42,"category":10,"createdAt":43,"date":44,"description":45,"image":15,"imageAlt":15,"readingTime":31,"tags":46,"title":50,"updatedAt":51,"categoryLegacy":34},"6a3389c9aa6b273b0c5c95c6","crowdin-auto-translate-injecte-lang-auto-signal-de-langue-casse-sur-tout-le-site","https://seogard.io/blog/crowdin-auto-translate-injecte-lang-auto-signal-de-langue-casse-sur-tout-le-site","2026-06-18T06:01:45.023Z","2026-06-18","Crowdin auto-translate injecte lang=\\\"auto\\\" sur tout le site. Google confond le marché cible. Récit, diagnostic et fix complet.",[47,34,48,49],"crowdin","lang","translation","Crowdin lang=\\\"auto\\\" : signal de langue cassé, −34 % trafic","Thu Jun 18 2026 06:01:45 GMT+0000 (Coordinated Universal Time)",{"_id":53,"slug":54,"__v":6,"author":7,"canonical":55,"category":10,"createdAt":56,"date":44,"description":57,"image":15,"imageAlt":15,"readingTime":31,"tags":58,"title":64,"updatedAt":65,"categoryLegacy":66},"6a34169aaa6b273b0ccfca84","for-site-moves-specify-all-domain-variants-with-google-s-change-of-address-tool","https://seogard.io/blog/for-site-moves-specify-all-domain-variants-with-google-s-change-of-address-tool","2026-06-18T16:02:34.751Z","Google clarifie sa doc sur les site moves : chaque variante de domaine doit être déclarée dans le Change of Address tool. Guide technique complet.",[59,60,61,62,63],"site move","domain variants","Change of Address","Search Console","migration SEO","Change of Address : déclarer toutes les variantes de domaine","Thu Jun 18 2026 16:02:34 GMT+0000 (Coordinated Universal Time)","Actualités SEO",{"_id":68,"slug":69,"__v":6,"author":7,"canonical":70,"category":10,"createdAt":71,"date":72,"description":73,"image":15,"imageAlt":15,"readingTime":74,"tags":75,"title":80,"updatedAt":81,"categoryLegacy":82},"6a32c4e8aa6b273b0cbed17b","storyblok-redirections-en-custom-field-oubliees-au-passage-nouveau-plan","https://seogard.io/blog/storyblok-redirections-en-custom-field-oubliees-au-passage-nouveau-plan","2026-06-17T16:01:44.888Z","2026-06-17","Un site e-commerce perd 1 200 redirections stockées en custom field Storyblok lors d'un upgrade de plan. Récit, diagnostic et fix complet.",11,[76,77,78,79],"storyblok","redirects","migration","cms","Storyblok : redirections custom perdues après changement de plan","Wed Jun 17 2026 16:01:44 GMT+0000 (Coordinated Universal Time)","Headless",{"_id":84,"slug":85,"__v":6,"author":7,"canonical":86,"category":10,"createdAt":87,"date":88,"description":89,"image":15,"imageAlt":15,"readingTime":31,"tags":90,"title":95,"updatedAt":96,"categoryLegacy":82},"6a2f9542aa6b273b0c30f3ec","contentful-le-champ-seo-title-non-sync-vers-next-js-fallback-first-h1-genere","https://seogard.io/blog/contentful-le-champ-seo-title-non-sync-vers-next-js-fallback-first-h1-genere","2026-06-15T06:01:38.446Z","2026-06-15","Un champ SEO title Contentful non mappé dans Next.js génère un fallback H1 identique sur 1 200 variantes produit. Récit, diagnostic, fix.",[91,92,93,94],"contentful","headless","next.js","mapping","Contentful + Next.js : title manquant, fallback H1 sur 1 200 pages","Mon Jun 15 2026 06:01:38 GMT+0000 (Coordinated Universal Time)",{"_id":98,"slug":99,"__v":6,"author":7,"canonical":100,"category":10,"createdAt":101,"date":102,"description":103,"image":15,"imageAlt":15,"readingTime":31,"tags":104,"title":110,"updatedAt":111,"categoryLegacy":66},"6a2e441caa6b273b0c22bc85","what-apple-s-gemini-powered-siri-means-for-search-visibility-via-sejournal-mattgsouthern","https://seogard.io/blog/what-apple-s-gemini-powered-siri-means-for-search-visibility-via-sejournal-mattgsouthern","2026-06-14T06:03:08.037Z","2026-06-14","Apple intègre Gemini dans Siri. Analyse technique des conséquences pour le crawl, le rendering, le structured data et la visibilité organique de vos pages.",[105,106,107,108,109],"siri","gemini","apple-intelligence","llm-seo","search-visibility","Siri + Gemini : impact concret sur la visibilité SEO","Sun Jun 14 2026 06:03:08 GMT+0000 (Coordinated Universal Time)",{"categories":113},[114,117,121,125,129,133,137],{"category":10,"slug":115,"count":116},"regressions-seo",138,{"category":118,"slug":119,"count":120},"GEO / IA","geo-ia",98,{"category":122,"slug":123,"count":124},"Indexation & crawl","indexation-crawl",32,{"category":126,"slug":127,"count":128},"SSR / CSR","ssr-csr",23,{"category":130,"slug":131,"count":132},"Données structurées","donnees-structurees",6,{"category":134,"slug":135,"count":136},"CI/CD SEO","ci-cd-seo",4,{"category":138,"slug":139,"count":140},"Monitoring continu","monitoring-continu",3]