[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fBb7GqA3XtueBLGzCldMludGJsoPKybja_pAHitAv7T8":3,"$fe9t1dWnIj5EHrt8wix59l7GDczcBcrI0wdwj1ilHSoo":26},{"_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":24,"updatedAt":25},"69e31e7faa6b273b0c8b6be6","google-bans-back-button-hijacking-agentic-search-grows-seo-pulse-via-sejournal-mattgsouthern",0,"Equipe Seogard","Google vient d'ajouter le back button hijacking à sa liste officielle de violations spam. Ce n'est pas un simple ajustement de wording dans la documentation — c'est un signal que les rapports de spam utilisateurs peuvent désormais déclencher des actions manuelles contre les sites qui manipulent le bouton retour du navigateur. En parallèle, Google étend sa fonctionnalité de réservation de restaurants via l'agentic search à de nouveaux marchés, confirmant l'accélération de la recherche agentique.\n\n## Le back button hijacking : anatomie technique d'une manipulation\n\nLe back button hijacking exploite l'API History du navigateur pour piéger l'utilisateur dans une boucle. Quand l'utilisateur clique sur le bouton retour, au lieu de revenir à la page précédente (typiquement la SERP Google), il reste sur le même site ou atterrit sur une page interstitielle.\n\n### Comment ça fonctionne concrètement\n\nLe mécanisme repose sur `history.pushState()` ou `history.replaceState()`. Le script injecte des entrées fantômes dans l'historique du navigateur à l'arrivée de l'utilisateur, de sorte que le bouton retour consomme ces entrées fictives au lieu de ramener l'utilisateur à sa page d'origine.\n\nVoici un exemple simplifié du pattern le plus courant :\n\n```javascript\n// Pattern de back button hijacking — NE PAS IMPLÉMENTER\n// Injecte des entrées dans l'historique dès le chargement de la page\n(function() {\n  // Pousse plusieurs états dans l'historique\n  for (let i = 0; i \u003C 5; i++) {\n    history.pushState(\n      { hijack: true, index: i },\n      '',\n      window.location.href + '#stay-' + i\n    );\n  }\n\n  // Intercepte l'événement popstate (déclenché par le bouton retour)\n  window.addEventListener('popstate', function(event) {\n    if (event.state && event.state.hijack) {\n      // Redirige vers une page monétisée au lieu de laisser\n      // l'utilisateur revenir en arrière\n      window.location.href = '/offre-speciale?ref=back';\n    }\n  });\n})();\n```\n\nUne variante plus sophistiquée utilise `replaceState` combiné avec un `beforeunload` pour rendre la sortie encore plus difficile :\n\n```javascript\n// Variante avec replaceState — également considérée comme spam\nwindow.addEventListener('load', function() {\n  // Remplace l'état actuel pour contrôler le point de retour\n  history.replaceState({ trapped: true }, '', window.location.href);\n  history.pushState({ trapped: true }, '', window.location.href);\n\n  window.addEventListener('popstate', function() {\n    // Re-pousse un état à chaque tentative de retour\n    history.pushState({ trapped: true }, '', window.location.href);\n\n    // Affiche un overlay \"Attendez, voici une offre...\"\n    document.getElementById('exit-intent-overlay').style.display = 'block';\n  });\n});\n```\n\n### Pourquoi Google agit maintenant\n\nCe type de manipulation existe depuis plus d'une décennie. Les sites de couponing, les comparateurs agressifs et certains éditeurs de contenu viral l'utilisaient pour gonfler artificiellement le temps passé sur le site et les pages vues par session — deux métriques qui influencent les revenus publicitaires.\n\nLe timing de cette classification comme violation spam n'est pas anodin. Google dispose désormais de données comportementales plus granulaires via Chrome (Chrome User Experience Report, interactions signals). Un utilisateur qui clique 4 ou 5 fois sur le bouton retour sans jamais quitter un site génère un pattern de frustration détectable côté navigateur. Ce signal, agrégé à l'échelle de Chrome, donne à Google une vue directe sur les sites qui piègent les utilisateurs.\n\nLa documentation mise à jour de Google sur les [spam policies](https://developers.google.com/search/docs/essentials/spam-policies) inclut désormais explicitement cette pratique aux côtés du cloaking, des doorway pages et du link spam.\n\n## Détecter le back button hijacking sur vos propriétés\n\nMême si vous n'implémentez pas volontairement ce pattern, des scripts tiers (ad networks, widgets, analytics exotiques) peuvent l'introduire à votre insu. Un tag manager mal configuré qui charge un script d'un partenaire peu scrupuleux suffit.\n\n### Audit via Chrome DevTools\n\nOuvrez les DevTools (F12), onglet **Sources**, puis filtrez les event listeners sur `popstate` :\n\n1. Naviguez sur la page suspecte\n2. Ouvrez la console DevTools\n3. Exécutez ce snippet de diagnostic :\n\n```javascript\n// Diagnostic : détecter les manipulations de l'historique\n(function detectHistoryHijack() {\n  const originalPushState = history.pushState;\n  const originalReplaceState = history.replaceState;\n  let pushCount = 0;\n  let replaceCount = 0;\n\n  history.pushState = function() {\n    pushCount++;\n    console.warn(\n      `[HISTORY AUDIT] pushState appelé (total: ${pushCount})`,\n      '\\nArguments:', arguments,\n      '\\nStack:', new Error().stack\n    );\n    return originalPushState.apply(this, arguments);\n  };\n\n  history.replaceState = function() {\n    replaceCount++;\n    console.warn(\n      `[HISTORY AUDIT] replaceState appelé (total: ${replaceCount})`,\n      '\\nArguments:', arguments,\n      '\\nStack:', new Error().stack\n    );\n    return originalReplaceState.apply(this, arguments);\n  };\n\n  // Vérifie aussi les listeners popstate existants\n  const listeners = getEventListeners(window);\n  if (listeners.popstate && listeners.popstate.length > 0) {\n    console.warn(\n      `[HISTORY AUDIT] ${listeners.popstate.length} listener(s) popstate détecté(s):`,\n      listeners.popstate.map(l => l.listener.toString().substring(0, 200))\n    );\n  }\n\n  console.log('[HISTORY AUDIT] Monitoring activé. Naviguez normalement.');\n})();\n```\n\nCe script monkey-patche `pushState` et `replaceState` pour logger chaque appel avec sa stack trace. Vous identifiez immédiatement quel script est responsable.\n\n### Audit à l'échelle avec Puppeteer\n\nPour un site e-commerce de 15 000 pages, l'audit manuel ne passe pas à l'échelle. Voici un script Puppeteer qui crawle un échantillon de pages et détecte les manipulations d'historique automatiquement :\n\n```javascript\n// audit-history-hijack.mjs\nimport puppeteer from 'puppeteer';\n\nconst URLS_TO_TEST = [\n  'https://shop.example.fr/categorie/chaussures',\n  'https://shop.example.fr/produit/nike-air-max-90',\n  'https://shop.example.fr/promo/soldes-ete',\n  // ... charger depuis un sitemap ou un export Screaming Frog\n];\n\nasync function auditPage(browser, url) {\n  const page = await browser.newPage();\n  const historyEvents = [];\n\n  // Injecte le monitoring avant tout script de la page\n  await page.evaluateOnNewDocument(() => {\n    const orig = history.pushState.bind(history);\n    let count = 0;\n    history.pushState = function(...args) {\n      count++;\n      window.__historyPushCount = count;\n      window.__lastPushStack = new Error().stack;\n      return orig(...args);\n    };\n  });\n\n  await page.goto(url, { waitUntil: 'networkidle2', timeout: 15000 });\n  // Attendre 3 secondes pour les scripts lazy\n  await new Promise(r => setTimeout(r, 3000));\n\n  const pushCount = await page.evaluate(() => window.__historyPushCount || 0);\n  const hasPopstateListener = await page.evaluate(() => {\n    // Heuristique : tenter un back et voir si l'URL change\n    return new Promise(resolve => {\n      const originalHref = window.location.href;\n      window.addEventListener('popstate', () => {\n        resolve(window.location.href === originalHref);\n      }, { once: true });\n      history.back();\n      setTimeout(() => resolve(false), 1000);\n    });\n  });\n\n  const result = {\n    url,\n    pushStateCallsOnLoad: pushCount,\n    possibleHijack: pushCount > 2 || hasPopstateListener,\n  };\n\n  await page.close();\n  return result;\n}\n\n(async () => {\n  const browser = await puppeteer.launch({ headless: 'new' });\n  const results = [];\n\n  for (const url of URLS_TO_TEST) {\n    try {\n      const result = await auditPage(browser, url);\n      results.push(result);\n      if (result.possibleHijack) {\n        console.error(`⚠️  SUSPECT: ${url} (${result.pushStateCallsOnLoad} pushState calls)`);\n      }\n    } catch (e) {\n      console.error(`Erreur sur ${url}: ${e.message}`);\n    }\n  }\n\n  await browser.close();\n\n  const suspects = results.filter(r => r.possibleHijack);\n  console.log(`\\nAudit terminé: ${suspects.length}/${results.length} pages suspectes`);\n  console.log(JSON.stringify(suspects, null, 2));\n})();\n```\n\n### Ce que Screaming Frog ne voit pas\n\nScreaming Frog crawle en mode headless et n'exécute pas JavaScript par défaut. Même en activant le rendering JavaScript, il ne simule pas le bouton retour. C'est un angle mort classique : un crawl technique standard ne détectera jamais le back button hijacking. Vous avez besoin d'un audit comportemental, pas d'un crawl structurel.\n\nC'est exactement le type de régression qu'un outil de monitoring continu comme Seogard peut capturer. Un script tiers ajouté mardi qui injecte des `pushState` sera détecté lors du prochain scan de rendering, avant qu'un utilisateur ne soumette un rapport de spam à Google.\n\n## Spam reports et manual actions : le mécanisme de sanction\n\nGoogle précise que les rapports de spam soumis par les utilisateurs via le [formulaire dédié](https://search.google.com/search-console/spam-report) peuvent désormais déclencher des actions manuelles contre les sites identifiés comme pratiquant le back button hijacking.\n\n### La chaîne de conséquences\n\nLe processus est le suivant :\n\n1. Un utilisateur expérimente le piège du bouton retour\n2. Il soumet un spam report via le formulaire Google\n3. L'équipe webspam de Google examine le site\n4. Si confirmé, une manual action est appliquée dans Search Console\n5. Les pages affectées (ou le site entier) sont déclassées ou désindexées\n\nLa différence avec une action algorithmique est capitale : une manual action nécessite une demande de réexamen manuelle après correction. Le délai moyen de traitement d'une demande de réexamen varie de quelques jours à plusieurs semaines. Pendant ce temps, le trafic organique chute.\n\n### Scénario concret : un comparateur d'assurances\n\nPrenons un comparateur d'assurances avec 8 000 pages indexées qui génère 45 000 visites organiques mensuelles. Un partenaire publicitaire fournit un script \"d'engagement\" qui, en réalité, injecte 3 `pushState` à chaque page vue et redirige les utilisateurs vers une landing page de conversion quand ils tentent de revenir en arrière.\n\nLe site reçoit une manual action type \"user experience violations\" dans Search Console. Le trafic organique chute de 70% en 48 heures. L'équipe SEO met 4 jours à identifier le script responsable (il est obfusqué et chargé via un tag manager côté partenaire), 2 jours à le retirer et redéployer, puis 12 jours à obtenir la levée de la manual action après soumission de la demande de réexamen.\n\nBilan : 18 jours de trafic dégradé, soit environ 27 000 visites organiques perdues. À un CPC moyen de 2,80€ dans le secteur assurance, c'est l'équivalent de 75 600€ de trafic SEO évaporé.\n\n### Vérifier l'absence de manual action\n\nDans Google Search Console, naviguez vers **Sécurité et actions manuelles > Actions manuelles**. Si le champ affiche \"Aucun problème détecté\", vous êtes clean. Mais ne vous arrêtez pas là — vérifiez aussi l'onglet **Problèmes de sécurité** qui peut signaler des scripts injectés par des tiers compromis.\n\n## L'agentic search s'étend : ce que ça change pour le SEO technique\n\nEn parallèle de l'annonce sur le back button hijacking, Google étend sa fonctionnalité de réservation de restaurants via l'agentic search à de nouveaux marchés. L'agent AI de Google peut désormais effectuer une réservation complète — choix du restaurant, sélection du créneau, confirmation — sans que l'utilisateur ne quitte l'interface de recherche.\n\n### Du lien bleu à l'action complétée\n\nCe n'est plus de la recherche au sens classique. L'utilisateur ne visite plus le site du restaurant. L'agent interagit directement avec les API de réservation (OpenTable, TheFork, systèmes propriétaires) pour accomplir la tâche.\n\nLes implications SEO sont profondes : si l'agent peut accomplir la tâche sans envoyer l'utilisateur sur votre site, le trafic organique de l'étape \"recherche d'information\" disparaît. Vous devez être la source de données que l'agent consomme, pas la page que l'utilisateur visite.\n\nNous avons déjà analysé cette tendance dans notre article sur [l'agentic search et le nouveau playbook SEO](/blog/agentic-engine-optimization-google-ai-director-outlines-new-content-playbook), ainsi que dans notre analyse sur [la recherche agentique qui disrupte le SEO aujourd'hui](/blog/google-s-task-based-agentic-search-is-disrupting-seo-today-not-tomorrow-via-sejournal-martinibuster).\n\n### Structured data : le ticket d'entrée pour l'agentic search\n\nPour qu'un agent AI puisse interagir avec votre contenu, vos données doivent être structurées et accessibles via des formats machine-readable. Dans le cas de la restauration, c'est le schema `Restaurant` combiné avec `FoodEstablishmentReservation` :\n\n```html\n\u003Cscript type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Restaurant\",\n  \"name\": \"Le Comptoir du Marais\",\n  \"address\": {\n    \"@type\": \"PostalAddress\",\n    \"streetAddress\": \"42 Rue des Archives\",\n    \"addressLocality\": \"Paris\",\n    \"postalCode\": \"75004\",\n    \"addressCountry\": \"FR\"\n  },\n  \"servesCuisine\": \"Cuisine française contemporaine\",\n  \"priceRange\": \"€€€\",\n  \"acceptsReservations\": true,\n  \"potentialAction\": {\n    \"@type\": \"ReserveAction\",\n    \"target\": {\n      \"@type\": \"EntryPoint\",\n      \"urlTemplate\": \"https://lecomptoirdumarais.fr/reservation?date={date}&partySize={partySize}\",\n      \"actionPlatform\": [\n        \"http://schema.org/DesktopWebPlatform\",\n        \"http://schema.org/MobileWebPlatform\"\n      ]\n    },\n    \"result\": {\n      \"@type\": \"FoodEstablishmentReservation\",\n      \"name\": \"Réservation au Comptoir du Marais\"\n    }\n  },\n  \"openingHoursSpecification\": [\n    {\n      \"@type\": \"OpeningHoursSpecification\",\n      \"dayOfWeek\": [\"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\n      \"opens\": \"12:00\",\n      \"closes\": \"14:30\"\n    },\n    {\n      \"@type\": \"OpeningHoursSpecification\",\n      \"dayOfWeek\": [\"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\n      \"opens\": \"19:00\",\n      \"closes\": \"22:30\"\n    }\n  ]\n}\n\u003C/script>\n```\n\nLe `potentialAction` de type `ReserveAction` est le point critique : c'est ce qui permet à l'agent de déclencher une action concrète. Sans ce markup, votre restaurant existe dans le knowledge graph mais n'est pas \"actionable\" par l'agent.\n\n### Au-delà de la restauration\n\nL'extension de l'agentic search à la réservation de restaurants n'est que la partie visible. Le pattern se généralise à tous les secteurs transactionnels : réservation d'hôtels, prise de rendez-vous médicaux, achat de billets, souscription d'abonnements.\n\nPour les sites e-commerce et SaaS, la question devient : votre site est-il conçu pour être consommé par des agents ? Notre analyse sur [comment les agents AI voient votre site](/blog/how-ai-agents-see-your-website-and-how-to-build-for-them-via-sejournal-slobodanmanic) et sur [l'architecture machine-first](/blog/machine-first-architecture-ai-agents-are-here-and-your-website-isn-t-ready-says-no-hacks-podcast-host-via-sejournal-theshelleywalsh) détaille les adaptations techniques nécessaires.\n\n## Intersection des deux annonces : la cohérence de la stratégie Google\n\nCes deux annonces — la classification du back button hijacking comme spam et l'expansion de l'agentic search — ne sont pas des nouvelles isolées. Elles dessinent une même direction.\n\n### Google protège la boucle de confiance\n\nL'agentic search ne fonctionne que si les utilisateurs font confiance à Google pour exécuter des actions en leur nom. Si un agent réserve un restaurant et que l'utilisateur se retrouve piégé sur un site qui manipule sa navigation, la confiance dans l'ensemble du système s'effondre.\n\nEn éliminant les sites qui dégradent l'expérience utilisateur au niveau de la navigation, Google assainit l'écosystème pour rendre l'agentic search viable. Les sites qui jouent le jeu — contenu structuré, navigation clean, données fiables — deviennent les fournisseurs privilégiés de l'agent.\n\n### Le crawl des agents AI et la qualité du site\n\nLes bots AI qui crawlent votre site pour alimenter les réponses agentiques évaluent la qualité technique globale. Un site qui manipule l'historique du navigateur envoie un signal négatif qui dépasse le cadre du spam classique — il remet en question la fiabilité des données que le site fournit.\n\nNous avons documenté comment les bots IA crawlent les sites dans [notre analyse sur le comportement des crawlers AI](/blog/llms-et-crawl-comment-les-bots-ia-crawlent-votre-site). La synergie entre qualité technique et visibilité dans les réponses agentiques se renforce.\n\n## Plan d'action : audit et prévention\n\n### 1. Audit immédiat des scripts tiers\n\nListez tous les scripts tiers chargés sur votre site via le tag manager. Pour chacun, vérifiez s'il accède à l'API History :\n\n```bash\n# Extraire tous les scripts inline et externes d'une page\ncurl -s https://shop.example.fr/ | \\\n  grep -oP '(?\u003C=src=\")[^\"]+\\.js' | \\\n  while read script; do\n    echo \"=== Vérification: $script ===\"\n    curl -s \"$script\" | grep -n 'pushState\\|replaceState\\|popstate' || echo \"  Clean\"\n  done\n```\n\nCe script Bash télécharge chaque fichier JavaScript référencé sur la page et recherche les appels à `pushState`, `replaceState` et les listeners `popstate`. C'est un premier filtre grossier — un résultat positif nécessite une analyse manuelle pour distinguer un usage légitime (SPA routing) d'un hijacking.\n\n### 2. Distinguer usage légitime et abusif\n\nL'API History est parfaitement légitime pour le routing côté client dans les SPA (React Router, Vue Router, etc.). La différence :\n\n- **Légitime** : `pushState` est appelé en réponse à une action utilisateur (clic sur un lien interne) et l'URL change effectivement\n- **Abusif** : `pushState` est appelé automatiquement au chargement de la page, souvent plusieurs fois, sans changement d'URL réel ou avec des fragments/hash artificiels\n\nLe critère simple : si votre `pushState` est dans un `DOMContentLoaded` ou un `load` event sans interaction utilisateur préalable, c'est un red flag.\n\n### 3. Content Security Policy comme garde-fou\n\nUne CSP stricte peut empêcher les scripts tiers non autorisés d'accéder à l'API History :\n\n```nginx\n# Configuration Nginx — CSP stricte avec reporting\nadd_header Content-Security-Policy \"\n  default-src 'self';\n  script-src 'self' https://www.googletagmanager.com https://cdn.trusted-partner.com;\n  report-uri /csp-violation-report;\n  report-to csp-endpoint;\n\" always;\n\n# Endpoint de reporting pour surveiller les violations\nadd_header Report-To '{\n  \"group\": \"csp-endpoint\",\n  \"max_age\": 86400,\n  \"endpoints\": [{\"url\": \"https://shop.example.fr/csp-violation-report\"}]\n}' always;\n```\n\nEn limitant `script-src` aux domaines de confiance, vous bloquez l'exécution de scripts tiers non autorisés. Le `report-uri` vous alerte quand un script bloqué tente de s'exécuter — ce qui peut révéler une injection ou un tag manager compromis.\n\n### 4. Monitoring continu\n\nLe back button hijacking peut apparaître du jour au lendemain via une mise à jour de script tiers. Un audit ponctuel ne suffit pas. Intégrez la vérification de l'API History dans votre pipeline de monitoring SEO technique. Un outil comme Seogard qui scanne le comportement JavaScript en continu détectera l'apparition de `pushState` non légitime avant que Google ne reçoive un spam report.\n\n### 5. Préparer votre site pour l'agentic search\n\nEn parallèle de la correction des problèmes de navigation :\n\n- Auditez vos structured data avec le [test de résultats enrichis de Google](https://search.google.com/test/rich-results)\n- Implémentez les `potentialAction` pertinents pour votre secteur\n- Assurez-vous que votre contenu est accessible sans JavaScript pour les crawlers — voir notre article sur le [rendering budget de Google](/blog/rendering-budget-de-google-combien-de-javascript-est-trop)\n- Exposez vos données via des formats structurés que les agents peuvent consommer programmatiquement\n\nL'évolution vers l'agentic search impacte aussi votre stratégie de visibilité locale, comme nous l'avons analysé dans [pourquoi votre site est la source de vérité pour la recherche AI locale](/blog/why-your-website-is-now-the-source-of-truth-in-local-ai-search).\n\n## Ce qu'il faut retenir\n\nLe back button hijacking rejoint la liste des manipulations qui déclenchent des manual actions. Si vous chargez des scripts tiers — et tous les sites en chargent — auditez-les maintenant, pas après avoir reçu la notification dans Search Console. En parallèle, l'agentic search de Google passe de l'expérimentation à l'expansion géographique : les sites qui structurent leurs données pour être actionnables par des agents prennent un avantage concurrentiel durable. Les deux annonces pointent dans la même direction — Google récompense les sites techniquement irréprochables et pénalise ceux qui sacrifient l'expérience utilisateur pour des métriques artificielles.","https://seogard.io/blog/google-bans-back-button-hijacking-agentic-search-grows-seo-pulse-via-sejournal-mattgsouthern","Actualités SEO","2026-04-18T06:02:39.791Z","2026-04-18","Google interdit le back button hijacking comme spam. Analyse technique des mécanismes, détection, et impact sur l'agentic search pour les SEO.","\u003Cp>Google vient d'ajouter le back button hijacking à sa liste officielle de violations spam. Ce n'est pas un simple ajustement de wording dans la documentation — c'est un signal que les rapports de spam utilisateurs peuvent désormais déclencher des actions manuelles contre les sites qui manipulent le bouton retour du navigateur. En parallèle, Google étend sa fonctionnalité de réservation de restaurants via l'agentic search à de nouveaux marchés, confirmant l'accélération de la recherche agentique.\u003C/p>\n\u003Ch2>Le back button hijacking : anatomie technique d'une manipulation\u003C/h2>\n\u003Cp>Le back button hijacking exploite l'API History du navigateur pour piéger l'utilisateur dans une boucle. Quand l'utilisateur clique sur le bouton retour, au lieu de revenir à la page précédente (typiquement la SERP Google), il reste sur le même site ou atterrit sur une page interstitielle.\u003C/p>\n\u003Ch3>Comment ça fonctionne concrètement\u003C/h3>\n\u003Cp>Le mécanisme repose sur \u003Ccode>history.pushState()\u003C/code> ou \u003Ccode>history.replaceState()\u003C/code>. Le script injecte des entrées fantômes dans l'historique du navigateur à l'arrivée de l'utilisateur, de sorte que le bouton retour consomme ces entrées fictives au lieu de ramener l'utilisateur à sa page d'origine.\u003C/p>\n\u003Cp>Voici un exemple simplifié du pattern le plus courant :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// Pattern de back button hijacking — NE PAS IMPLÉMENTER\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// Injecte des entrées dans l'historique dès le chargement de la page\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">function\u003C/span>\u003Cspan style=\"color:#E1E4E8\">() {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // Pousse plusieurs états dans l'historique\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"color:#F97583\">let\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> i \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; i \u003C/span>\u003Cspan style=\"color:#F97583\">&#x3C;\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 5\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; i\u003C/span>\u003Cspan style=\"color:#F97583\">++\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    history.\u003C/span>\u003Cspan style=\"color:#B392F0\">pushState\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      { hijack: \u003C/span>\u003Cspan style=\"color:#79B8FF\">true\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, index: i },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      ''\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      window.location.href \u003C/span>\u003Cspan style=\"color:#F97583\">+\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '#stay-'\u003C/span>\u003Cspan style=\"color:#F97583\"> +\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> i\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\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // Intercepte l'événement popstate (déclenché par le bouton retour)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  window.\u003C/span>\u003Cspan style=\"color:#B392F0\">addEventListener\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'popstate'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#F97583\">function\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">event\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\"> (event.state \u003C/span>\u003Cspan style=\"color:#F97583\">&#x26;&#x26;\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> event.state.hijack) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">      // Redirige vers une page monétisée au lieu de laisser\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">      // l'utilisateur revenir en arrière\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      window.location.href \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '/offre-speciale?ref=back'\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:#E1E4E8\">})();\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Une variante plus sophistiquée utilise \u003Ccode>replaceState\u003C/code> combiné avec un \u003Ccode>beforeunload\u003C/code> pour rendre la sortie encore plus difficile :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// Variante avec replaceState — également considérée comme spam\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">window.\u003C/span>\u003Cspan style=\"color:#B392F0\">addEventListener\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'load'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#F97583\">function\u003C/span>\u003Cspan style=\"color:#E1E4E8\">() {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // Remplace l'état actuel pour contrôler le point de retour\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  history.\u003C/span>\u003Cspan style=\"color:#B392F0\">replaceState\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({ trapped: \u003C/span>\u003Cspan style=\"color:#79B8FF\">true\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> }, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">''\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, window.location.href);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  history.\u003C/span>\u003Cspan style=\"color:#B392F0\">pushState\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({ trapped: \u003C/span>\u003Cspan style=\"color:#79B8FF\">true\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> }, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">''\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, window.location.href);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  window.\u003C/span>\u003Cspan style=\"color:#B392F0\">addEventListener\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'popstate'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#F97583\">function\u003C/span>\u003Cspan style=\"color:#E1E4E8\">() {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    // Re-pousse un état à chaque tentative de retour\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    history.\u003C/span>\u003Cspan style=\"color:#B392F0\">pushState\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({ trapped: \u003C/span>\u003Cspan style=\"color:#79B8FF\">true\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> }, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">''\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, window.location.href);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    // Affiche un overlay \"Attendez, voici une offre...\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    document.\u003C/span>\u003Cspan style=\"color:#B392F0\">getElementById\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'exit-intent-overlay'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">).style.display \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'block'\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>\u003C/code>\u003C/pre>\n\u003Ch3>Pourquoi Google agit maintenant\u003C/h3>\n\u003Cp>Ce type de manipulation existe depuis plus d'une décennie. Les sites de couponing, les comparateurs agressifs et certains éditeurs de contenu viral l'utilisaient pour gonfler artificiellement le temps passé sur le site et les pages vues par session — deux métriques qui influencent les revenus publicitaires.\u003C/p>\n\u003Cp>Le timing de cette classification comme violation spam n'est pas anodin. Google dispose désormais de données comportementales plus granulaires via Chrome (Chrome User Experience Report, interactions signals). Un utilisateur qui clique 4 ou 5 fois sur le bouton retour sans jamais quitter un site génère un pattern de frustration détectable côté navigateur. Ce signal, agrégé à l'échelle de Chrome, donne à Google une vue directe sur les sites qui piègent les utilisateurs.\u003C/p>\n\u003Cp>La documentation mise à jour de Google sur les \u003Ca href=\"https://developers.google.com/search/docs/essentials/spam-policies\">spam policies\u003C/a> inclut désormais explicitement cette pratique aux côtés du cloaking, des doorway pages et du link spam.\u003C/p>\n\u003Ch2>Détecter le back button hijacking sur vos propriétés\u003C/h2>\n\u003Cp>Même si vous n'implémentez pas volontairement ce pattern, des scripts tiers (ad networks, widgets, analytics exotiques) peuvent l'introduire à votre insu. Un tag manager mal configuré qui charge un script d'un partenaire peu scrupuleux suffit.\u003C/p>\n\u003Ch3>Audit via Chrome DevTools\u003C/h3>\n\u003Cp>Ouvrez les DevTools (F12), onglet \u003Cstrong>Sources\u003C/strong>, puis filtrez les event listeners sur \u003Ccode>popstate\u003C/code> :\u003C/p>\n\u003Col>\n\u003Cli>Naviguez sur la page suspecte\u003C/li>\n\u003Cli>Ouvrez la console DevTools\u003C/li>\n\u003Cli>Exécutez ce snippet de diagnostic :\u003C/li>\n\u003C/ol>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// Diagnostic : détecter les manipulations de l'historique\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">function\u003C/span>\u003Cspan style=\"color:#B392F0\"> detectHistoryHijack\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\"> originalPushState\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> history.pushState;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> originalReplaceState\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> history.replaceState;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  let\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> pushCount \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  let\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> replaceCount \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  history.\u003C/span>\u003Cspan style=\"color:#B392F0\">pushState\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> function\u003C/span>\u003Cspan style=\"color:#E1E4E8\">() {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    pushCount\u003C/span>\u003Cspan style=\"color:#F97583\">++\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">warn\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      `[HISTORY AUDIT] pushState appelé (total: ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">pushCount\u003C/span>\u003Cspan style=\"color:#9ECBFF\">})`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      '\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\n\u003C/span>\u003Cspan style=\"color:#9ECBFF\">Arguments:'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">arguments\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      '\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\n\u003C/span>\u003Cspan style=\"color:#9ECBFF\">Stack:'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#F97583\">new\u003C/span>\u003Cspan style=\"color:#B392F0\"> Error\u003C/span>\u003Cspan style=\"color:#E1E4E8\">().stack\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    );\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> originalPushState.\u003C/span>\u003Cspan style=\"color:#B392F0\">apply\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">this\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">arguments\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:#E1E4E8\">  history.\u003C/span>\u003Cspan style=\"color:#B392F0\">replaceState\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> function\u003C/span>\u003Cspan style=\"color:#E1E4E8\">() {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    replaceCount\u003C/span>\u003Cspan style=\"color:#F97583\">++\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">warn\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      `[HISTORY AUDIT] replaceState appelé (total: ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">replaceCount\u003C/span>\u003Cspan style=\"color:#9ECBFF\">})`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      '\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\n\u003C/span>\u003Cspan style=\"color:#9ECBFF\">Arguments:'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">arguments\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      '\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\n\u003C/span>\u003Cspan style=\"color:#9ECBFF\">Stack:'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#F97583\">new\u003C/span>\u003Cspan style=\"color:#B392F0\"> Error\u003C/span>\u003Cspan style=\"color:#E1E4E8\">().stack\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    );\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> originalReplaceState.\u003C/span>\u003Cspan style=\"color:#B392F0\">apply\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">this\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">arguments\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\">  // Vérifie aussi les listeners popstate existants\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> listeners\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#B392F0\"> getEventListeners\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(window);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (listeners.popstate \u003C/span>\u003Cspan style=\"color:#F97583\">&#x26;&#x26;\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> listeners.popstate.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#F97583\"> >\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">warn\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      `[HISTORY AUDIT] ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">listeners\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">popstate\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} listener(s) popstate détecté(s):`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      listeners.popstate.\u003C/span>\u003Cspan style=\"color:#B392F0\">map\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">l\u003C/span>\u003Cspan style=\"color:#F97583\"> =>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> l.listener.\u003C/span>\u003Cspan style=\"color:#B392F0\">toString\u003C/span>\u003Cspan style=\"color:#E1E4E8\">().\u003C/span>\u003Cspan style=\"color:#B392F0\">substring\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">200\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\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'[HISTORY AUDIT] Monitoring activé. Naviguez normalement.'\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 monkey-patche \u003Ccode>pushState\u003C/code> et \u003Ccode>replaceState\u003C/code> pour logger chaque appel avec sa stack trace. Vous identifiez immédiatement quel script est responsable.\u003C/p>\n\u003Ch3>Audit à l'échelle avec Puppeteer\u003C/h3>\n\u003Cp>Pour un site e-commerce de 15 000 pages, l'audit manuel ne passe pas à l'échelle. Voici un script Puppeteer qui crawle un échantillon de pages et détecte les manipulations d'historique automatiquement :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// audit-history-hijack.mjs\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> puppeteer \u003C/span>\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'puppeteer'\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\"> URLS_TO_TEST\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  'https://shop.example.fr/categorie/chaussures'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  'https://shop.example.fr/produit/nike-air-max-90'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  'https://shop.example.fr/promo/soldes-ete'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // ... charger depuis un sitemap ou un export Screaming Frog\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:#F97583\">async\u003C/span>\u003Cspan style=\"color:#F97583\"> function\u003C/span>\u003Cspan style=\"color:#B392F0\"> auditPage\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">browser\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">url\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\"> historyEvents\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // Injecte le monitoring avant tout script de la page\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> page.\u003C/span>\u003Cspan style=\"color:#B392F0\">evaluateOnNewDocument\u003C/span>\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:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> orig\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> history.pushState.\u003C/span>\u003Cspan style=\"color:#B392F0\">bind\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(history);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    let\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> count \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    history.\u003C/span>\u003Cspan style=\"color:#B392F0\">pushState\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> function\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">...\u003C/span>\u003Cspan style=\"color:#FFAB70\">args\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      count\u003C/span>\u003Cspan style=\"color:#F97583\">++\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      window.__historyPushCount \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> count;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      window.__lastPushStack \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#F97583\"> new\u003C/span>\u003Cspan style=\"color:#B392F0\"> Error\u003C/span>\u003Cspan style=\"color:#E1E4E8\">().stack;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      return\u003C/span>\u003Cspan style=\"color:#B392F0\"> orig\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">...\u003C/span>\u003Cspan style=\"color:#E1E4E8\">args);\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\">\u003C/span>\n\u003Cspan class=\"line\">\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\">(url, { waitUntil: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'networkidle2'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, timeout: \u003C/span>\u003Cspan style=\"color:#79B8FF\">15000\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  // Attendre 3 secondes pour les scripts lazy\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  await\u003C/span>\u003Cspan style=\"color:#F97583\"> new\u003C/span>\u003Cspan style=\"color:#79B8FF\"> Promise\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">r\u003C/span>\u003Cspan style=\"color:#F97583\"> =>\u003C/span>\u003Cspan style=\"color:#B392F0\"> setTimeout\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(r, \u003C/span>\u003Cspan style=\"color:#79B8FF\">3000\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\"> pushCount\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\">evaluate\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(() \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> window.__historyPushCount \u003C/span>\u003Cspan style=\"color:#F97583\">||\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\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\"> hasPopstateListener\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\">evaluate\u003C/span>\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:#6A737D\">    // Heuristique : tenter un back et voir si l'URL change\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C/span>\u003Cspan style=\"color:#F97583\"> new\u003C/span>\u003Cspan style=\"color:#79B8FF\"> Promise\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">resolve\u003C/span>\u003Cspan style=\"color:#F97583\"> =>\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\"> originalHref\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> window.location.href;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      window.\u003C/span>\u003Cspan style=\"color:#B392F0\">addEventListener\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'popstate'\u003C/span>\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:#B392F0\">        resolve\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(window.location.href \u003C/span>\u003Cspan style=\"color:#F97583\">===\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> originalHref);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      }, { once: \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\">      history.\u003C/span>\u003Cspan style=\"color:#B392F0\">back\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">      setTimeout\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(() \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#B392F0\"> resolve\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">false\u003C/span>\u003Cspan style=\"color:#E1E4E8\">), \u003C/span>\u003Cspan style=\"color:#79B8FF\">1000\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\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> result\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    url,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    pushStateCallsOnLoad: pushCount,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    possibleHijack: pushCount \u003C/span>\u003Cspan style=\"color:#F97583\">>\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 2\u003C/span>\u003Cspan style=\"color:#F97583\"> ||\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> hasPopstateListener,\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:#F97583\">  await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> page.\u003C/span>\u003Cspan style=\"color:#B392F0\">close\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> result;\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:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">async\u003C/span>\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:#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\"> puppeteer.\u003C/span>\u003Cspan style=\"color:#B392F0\">launch\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({ headless: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'new'\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\"> results\u003C/span>\u003Cspan style=\"color:#F97583\"> =\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\"> url\u003C/span>\u003Cspan style=\"color:#F97583\"> of\u003C/span>\u003Cspan style=\"color:#79B8FF\"> URLS_TO_TEST\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\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:#F97583\">      const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> result\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#B392F0\"> auditPage\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(browser, url);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      results.\u003C/span>\u003Cspan style=\"color:#B392F0\">push\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(result);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (result.possibleHijack) {\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\">`⚠️  SUSPECT: ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">url\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} (${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">result\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">pushStateCallsOnLoad\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} pushState calls)`\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>\u003Cspan style=\"color:#F97583\">catch\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (e) {\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\">`Erreur sur ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">url\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}: ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">e\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">message\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\">\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\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> suspects\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> results.\u003C/span>\u003Cspan style=\"color:#B392F0\">filter\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">r\u003C/span>\u003Cspan style=\"color:#F97583\"> =>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> r.possibleHijack);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\n\u003C/span>\u003Cspan style=\"color:#9ECBFF\">Audit terminé: ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">suspects\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}/${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">results\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} pages suspectes`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">JSON\u003C/span>\u003Cspan style=\"color:#E1E4E8\">.\u003C/span>\u003Cspan style=\"color:#B392F0\">stringify\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(suspects, \u003C/span>\u003Cspan style=\"color:#79B8FF\">null\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">2\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>Ce que Screaming Frog ne voit pas\u003C/h3>\n\u003Cp>Screaming Frog crawle en mode headless et n'exécute pas JavaScript par défaut. Même en activant le rendering JavaScript, il ne simule pas le bouton retour. C'est un angle mort classique : un crawl technique standard ne détectera jamais le back button hijacking. Vous avez besoin d'un audit comportemental, pas d'un crawl structurel.\u003C/p>\n\u003Cp>C'est exactement le type de régression qu'un outil de monitoring continu comme Seogard peut capturer. Un script tiers ajouté mardi qui injecte des \u003Ccode>pushState\u003C/code> sera détecté lors du prochain scan de rendering, avant qu'un utilisateur ne soumette un rapport de spam à Google.\u003C/p>\n\u003Ch2>Spam reports et manual actions : le mécanisme de sanction\u003C/h2>\n\u003Cp>Google précise que les rapports de spam soumis par les utilisateurs via le \u003Ca href=\"https://search.google.com/search-console/spam-report\">formulaire dédié\u003C/a> peuvent désormais déclencher des actions manuelles contre les sites identifiés comme pratiquant le back button hijacking.\u003C/p>\n\u003Ch3>La chaîne de conséquences\u003C/h3>\n\u003Cp>Le processus est le suivant :\u003C/p>\n\u003Col>\n\u003Cli>Un utilisateur expérimente le piège du bouton retour\u003C/li>\n\u003Cli>Il soumet un spam report via le formulaire Google\u003C/li>\n\u003Cli>L'équipe webspam de Google examine le site\u003C/li>\n\u003Cli>Si confirmé, une manual action est appliquée dans Search Console\u003C/li>\n\u003Cli>Les pages affectées (ou le site entier) sont déclassées ou désindexées\u003C/li>\n\u003C/ol>\n\u003Cp>La différence avec une action algorithmique est capitale : une manual action nécessite une demande de réexamen manuelle après correction. Le délai moyen de traitement d'une demande de réexamen varie de quelques jours à plusieurs semaines. Pendant ce temps, le trafic organique chute.\u003C/p>\n\u003Ch3>Scénario concret : un comparateur d'assurances\u003C/h3>\n\u003Cp>Prenons un comparateur d'assurances avec 8 000 pages indexées qui génère 45 000 visites organiques mensuelles. Un partenaire publicitaire fournit un script \"d'engagement\" qui, en réalité, injecte 3 \u003Ccode>pushState\u003C/code> à chaque page vue et redirige les utilisateurs vers une landing page de conversion quand ils tentent de revenir en arrière.\u003C/p>\n\u003Cp>Le site reçoit une manual action type \"user experience violations\" dans Search Console. Le trafic organique chute de 70% en 48 heures. L'équipe SEO met 4 jours à identifier le script responsable (il est obfusqué et chargé via un tag manager côté partenaire), 2 jours à le retirer et redéployer, puis 12 jours à obtenir la levée de la manual action après soumission de la demande de réexamen.\u003C/p>\n\u003Cp>Bilan : 18 jours de trafic dégradé, soit environ 27 000 visites organiques perdues. À un CPC moyen de 2,80€ dans le secteur assurance, c'est l'équivalent de 75 600€ de trafic SEO évaporé.\u003C/p>\n\u003Ch3>Vérifier l'absence de manual action\u003C/h3>\n\u003Cp>Dans Google Search Console, naviguez vers \u003Cstrong>Sécurité et actions manuelles > Actions manuelles\u003C/strong>. Si le champ affiche \"Aucun problème détecté\", vous êtes clean. Mais ne vous arrêtez pas là — vérifiez aussi l'onglet \u003Cstrong>Problèmes de sécurité\u003C/strong> qui peut signaler des scripts injectés par des tiers compromis.\u003C/p>\n\u003Ch2>L'agentic search s'étend : ce que ça change pour le SEO technique\u003C/h2>\n\u003Cp>En parallèle de l'annonce sur le back button hijacking, Google étend sa fonctionnalité de réservation de restaurants via l'agentic search à de nouveaux marchés. L'agent AI de Google peut désormais effectuer une réservation complète — choix du restaurant, sélection du créneau, confirmation — sans que l'utilisateur ne quitte l'interface de recherche.\u003C/p>\n\u003Ch3>Du lien bleu à l'action complétée\u003C/h3>\n\u003Cp>Ce n'est plus de la recherche au sens classique. L'utilisateur ne visite plus le site du restaurant. L'agent interagit directement avec les API de réservation (OpenTable, TheFork, systèmes propriétaires) pour accomplir la tâche.\u003C/p>\n\u003Cp>Les implications SEO sont profondes : si l'agent peut accomplir la tâche sans envoyer l'utilisateur sur votre site, le trafic organique de l'étape \"recherche d'information\" disparaît. Vous devez être la source de données que l'agent consomme, pas la page que l'utilisateur visite.\u003C/p>\n\u003Cp>Nous avons déjà analysé cette tendance dans notre article sur \u003Ca href=\"/blog/agentic-engine-optimization-google-ai-director-outlines-new-content-playbook\">l'agentic search et le nouveau playbook SEO\u003C/a>, ainsi que dans notre analyse sur \u003Ca href=\"/blog/google-s-task-based-agentic-search-is-disrupting-seo-today-not-tomorrow-via-sejournal-martinibuster\">la recherche agentique qui disrupte le SEO aujourd'hui\u003C/a>.\u003C/p>\n\u003Ch3>Structured data : le ticket d'entrée pour l'agentic search\u003C/h3>\n\u003Cp>Pour qu'un agent AI puisse interagir avec votre contenu, vos données doivent être structurées et accessibles via des formats machine-readable. Dans le cas de la restauration, c'est le schema \u003Ccode>Restaurant\u003C/code> combiné avec \u003Ccode>FoodEstablishmentReservation\u003C/code> :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"color:#85E89D\">script\u003C/span>\u003Cspan style=\"color:#B392F0\"> type\u003C/span>\u003Cspan style=\"color:#E1E4E8\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"application/ld+json\"\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\">  \"@context\": \"https://schema.org\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  \"@type\": \"Restaurant\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  \"name\": \"Le Comptoir du Marais\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  \"address\": {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \"@type\": \"PostalAddress\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \"streetAddress\": \"42 Rue des Archives\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \"addressLocality\": \"Paris\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \"postalCode\": \"75004\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \"addressCountry\": \"FR\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  \"servesCuisine\": \"Cuisine française contemporaine\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  \"priceRange\": \"€€€\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  \"acceptsReservations\": true,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  \"potentialAction\": {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \"@type\": \"ReserveAction\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    \"target\": {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"@type\": \"EntryPoint\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"urlTemplate\": \"https://lecomptoirdumarais.fr/reservation?date={date}&#x26;partySize={partySize}\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"actionPlatform\": [\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        \"http://schema.org/DesktopWebPlatform\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        \"http://schema.org/MobileWebPlatform\"\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:#E1E4E8\">    \"result\": {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"@type\": \"FoodEstablishmentReservation\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"name\": \"Réservation au Comptoir du Marais\"\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:#E1E4E8\">  \"openingHoursSpecification\": [\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"@type\": \"OpeningHoursSpecification\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"dayOfWeek\": [\"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"opens\": \"12:00\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"closes\": \"14:30\"\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:#E1E4E8\">      \"@type\": \"OpeningHoursSpecification\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"dayOfWeek\": [\"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"opens\": \"19:00\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      \"closes\": \"22:30\"\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:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">&#x3C;/\u003C/span>\u003Cspan style=\"color:#85E89D\">script\u003C/span>\u003Cspan style=\"color:#E1E4E8\">>\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Le \u003Ccode>potentialAction\u003C/code> de type \u003Ccode>ReserveAction\u003C/code> est le point critique : c'est ce qui permet à l'agent de déclencher une action concrète. Sans ce markup, votre restaurant existe dans le knowledge graph mais n'est pas \"actionable\" par l'agent.\u003C/p>\n\u003Ch3>Au-delà de la restauration\u003C/h3>\n\u003Cp>L'extension de l'agentic search à la réservation de restaurants n'est que la partie visible. Le pattern se généralise à tous les secteurs transactionnels : réservation d'hôtels, prise de rendez-vous médicaux, achat de billets, souscription d'abonnements.\u003C/p>\n\u003Cp>Pour les sites e-commerce et SaaS, la question devient : votre site est-il conçu pour être consommé par des agents ? Notre analyse sur \u003Ca href=\"/blog/how-ai-agents-see-your-website-and-how-to-build-for-them-via-sejournal-slobodanmanic\">comment les agents AI voient votre site\u003C/a> et sur \u003Ca href=\"/blog/machine-first-architecture-ai-agents-are-here-and-your-website-isn-t-ready-says-no-hacks-podcast-host-via-sejournal-theshelleywalsh\">l'architecture machine-first\u003C/a> détaille les adaptations techniques nécessaires.\u003C/p>\n\u003Ch2>Intersection des deux annonces : la cohérence de la stratégie Google\u003C/h2>\n\u003Cp>Ces deux annonces — la classification du back button hijacking comme spam et l'expansion de l'agentic search — ne sont pas des nouvelles isolées. Elles dessinent une même direction.\u003C/p>\n\u003Ch3>Google protège la boucle de confiance\u003C/h3>\n\u003Cp>L'agentic search ne fonctionne que si les utilisateurs font confiance à Google pour exécuter des actions en leur nom. Si un agent réserve un restaurant et que l'utilisateur se retrouve piégé sur un site qui manipule sa navigation, la confiance dans l'ensemble du système s'effondre.\u003C/p>\n\u003Cp>En éliminant les sites qui dégradent l'expérience utilisateur au niveau de la navigation, Google assainit l'écosystème pour rendre l'agentic search viable. Les sites qui jouent le jeu — contenu structuré, navigation clean, données fiables — deviennent les fournisseurs privilégiés de l'agent.\u003C/p>\n\u003Ch3>Le crawl des agents AI et la qualité du site\u003C/h3>\n\u003Cp>Les bots AI qui crawlent votre site pour alimenter les réponses agentiques évaluent la qualité technique globale. Un site qui manipule l'historique du navigateur envoie un signal négatif qui dépasse le cadre du spam classique — il remet en question la fiabilité des données que le site fournit.\u003C/p>\n\u003Cp>Nous avons documenté comment les bots IA crawlent les sites dans \u003Ca href=\"/blog/llms-et-crawl-comment-les-bots-ia-crawlent-votre-site\">notre analyse sur le comportement des crawlers AI\u003C/a>. La synergie entre qualité technique et visibilité dans les réponses agentiques se renforce.\u003C/p>\n\u003Ch2>Plan d'action : audit et prévention\u003C/h2>\n\u003Ch3>1. Audit immédiat des scripts tiers\u003C/h3>\n\u003Cp>Listez tous les scripts tiers chargés sur votre site via le tag manager. Pour chacun, vérifiez s'il accède à l'API History :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Extraire tous les scripts inline et externes d'une page\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://shop.example.fr/\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">  grep\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -oP\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '(?&#x3C;=src=\")[^\"]+\\.js'\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  while\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> read\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> script\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#F97583\">do\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"=== Vérification: \u003C/span>\u003Cspan style=\"color:#E1E4E8\">$script\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> ===\"\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\"> \"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$script\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> grep\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -n\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'pushState\\|replaceState\\|popstate'\u003C/span>\u003Cspan style=\"color:#F97583\"> ||\u003C/span>\u003Cspan style=\"color:#79B8FF\"> echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"  Clean\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  done\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Ce script Bash télécharge chaque fichier JavaScript référencé sur la page et recherche les appels à \u003Ccode>pushState\u003C/code>, \u003Ccode>replaceState\u003C/code> et les listeners \u003Ccode>popstate\u003C/code>. C'est un premier filtre grossier — un résultat positif nécessite une analyse manuelle pour distinguer un usage légitime (SPA routing) d'un hijacking.\u003C/p>\n\u003Ch3>2. Distinguer usage légitime et abusif\u003C/h3>\n\u003Cp>L'API History est parfaitement légitime pour le routing côté client dans les SPA (React Router, Vue Router, etc.). La différence :\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Légitime\u003C/strong> : \u003Ccode>pushState\u003C/code> est appelé en réponse à une action utilisateur (clic sur un lien interne) et l'URL change effectivement\u003C/li>\n\u003Cli>\u003Cstrong>Abusif\u003C/strong> : \u003Ccode>pushState\u003C/code> est appelé automatiquement au chargement de la page, souvent plusieurs fois, sans changement d'URL réel ou avec des fragments/hash artificiels\u003C/li>\n\u003C/ul>\n\u003Cp>Le critère simple : si votre \u003Ccode>pushState\u003C/code> est dans un \u003Ccode>DOMContentLoaded\u003C/code> ou un \u003Ccode>load\u003C/code> event sans interaction utilisateur préalable, c'est un red flag.\u003C/p>\n\u003Ch3>3. Content Security Policy comme garde-fou\u003C/h3>\n\u003Cp>Une CSP stricte peut empêcher les scripts tiers non autorisés d'accéder à l'API History :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Configuration Nginx — CSP stricte avec reporting\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Content-Security-Policy \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  default-src 'self';\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  script-src 'self' https://www.googletagmanager.com https://cdn.trusted-partner.com;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  report-uri /csp-violation-report;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  report-to csp-endpoint;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> always;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Endpoint de reporting pour surveiller les violations\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Report-To \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  \"group\": \"csp-endpoint\",\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  \"max_age\": 86400,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  \"endpoints\": [{\"url\": \"https://shop.example.fr/csp-violation-report\"}]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">}'\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> always;\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>En limitant \u003Ccode>script-src\u003C/code> aux domaines de confiance, vous bloquez l'exécution de scripts tiers non autorisés. Le \u003Ccode>report-uri\u003C/code> vous alerte quand un script bloqué tente de s'exécuter — ce qui peut révéler une injection ou un tag manager compromis.\u003C/p>\n\u003Ch3>4. Monitoring continu\u003C/h3>\n\u003Cp>Le back button hijacking peut apparaître du jour au lendemain via une mise à jour de script tiers. Un audit ponctuel ne suffit pas. Intégrez la vérification de l'API History dans votre pipeline de monitoring SEO technique. Un outil comme Seogard qui scanne le comportement JavaScript en continu détectera l'apparition de \u003Ccode>pushState\u003C/code> non légitime avant que Google ne reçoive un spam report.\u003C/p>\n\u003Ch3>5. Préparer votre site pour l'agentic search\u003C/h3>\n\u003Cp>En parallèle de la correction des problèmes de navigation :\u003C/p>\n\u003Cul>\n\u003Cli>Auditez vos structured data avec le \u003Ca href=\"https://search.google.com/test/rich-results\">test de résultats enrichis de Google\u003C/a>\u003C/li>\n\u003Cli>Implémentez les \u003Ccode>potentialAction\u003C/code> pertinents pour votre secteur\u003C/li>\n\u003Cli>Assurez-vous que votre contenu est accessible sans JavaScript pour les crawlers — voir notre article sur le \u003Ca href=\"/blog/rendering-budget-de-google-combien-de-javascript-est-trop\">rendering budget de Google\u003C/a>\u003C/li>\n\u003Cli>Exposez vos données via des formats structurés que les agents peuvent consommer programmatiquement\u003C/li>\n\u003C/ul>\n\u003Cp>L'évolution vers l'agentic search impacte aussi votre stratégie de visibilité locale, comme nous l'avons analysé dans \u003Ca href=\"/blog/why-your-website-is-now-the-source-of-truth-in-local-ai-search\">pourquoi votre site est la source de vérité pour la recherche AI locale\u003C/a>.\u003C/p>\n\u003Ch2>Ce qu'il faut retenir\u003C/h2>\n\u003Cp>Le back button hijacking rejoint la liste des manipulations qui déclenchent des manual actions. Si vous chargez des scripts tiers — et tous les sites en chargent — auditez-les maintenant, pas après avoir reçu la notification dans Search Console. En parallèle, l'agentic search de Google passe de l'expérimentation à l'expansion géographique : les sites qui structurent leurs données pour être actionnables par des agents prennent un avantage concurrentiel durable. Les deux annonces pointent dans la même direction — Google récompense les sites techniquement irréprochables et pénalise ceux qui sacrifient l'expérience utilisateur pour des métriques artificielles.\u003C/p>",null,12,[18,19,20,21,22,23],"google","spam","back button hijacking","agentic search","history API","manual action","Back Button Hijacking : Google en fait une violation spam","Sat Apr 18 2026 06:02:39 GMT+0000 (Coordinated Universal Time)",[27,42,55],{"_id":28,"slug":29,"__v":6,"author":7,"canonical":30,"category":10,"createdAt":31,"date":32,"description":33,"image":15,"imageAlt":15,"readingTime":16,"tags":34,"title":40,"updatedAt":41},"69e1cce1aa6b273b0c7d7064","why-log-file-analysis-matters-for-ai-crawlers-and-search-visibility","https://seogard.io/blog/why-log-file-analysis-matters-for-ai-crawlers-and-search-visibility","2026-04-17T06:02:09.095Z","2026-04-17","Analysez vos logs serveur pour tracer les crawlers IA, identifier les pages ignorées et optimiser votre visibilité dans les moteurs de réponse.",[35,36,37,38,39],"log file analysis","AI crawlers","crawl budget","search visibility","bot monitoring","Log file analysis pour AI crawlers : détecter ce que les bots IA ignorent","Fri Apr 17 2026 06:02:09 GMT+0000 (Coordinated Universal Time)",{"_id":43,"slug":44,"__v":6,"author":7,"canonical":45,"category":10,"createdAt":46,"date":32,"description":47,"image":15,"imageAlt":15,"readingTime":16,"tags":48,"title":53,"updatedAt":54},"69e2055faa6b273b0caa9ce6","machine-first-architecture-ai-agents-are-here-and-your-website-isn-t-ready-says-no-hacks-podcast-host-via-sejournal-theshelleywalsh","https://seogard.io/blog/machine-first-architecture-ai-agents-are-here-and-your-website-isn-t-ready-says-no-hacks-podcast-host-via-sejournal-theshelleywalsh","2026-04-17T10:03:11.013Z","Les AI agents ne crawlent pas comme Googlebot. Architecture, données structurées, API endpoints : guide technique pour rendre votre site lisible par les machines autonomes.",[49,50,51,52,21],"machine-first architecture","AI agents","SEO technique","données structurées","Machine-First Architecture : préparer votre site aux AI agents","Fri Apr 17 2026 10:03:11 GMT+0000 (Coordinated Universal Time)",{"_id":56,"slug":57,"__v":6,"author":7,"canonical":58,"category":10,"createdAt":59,"date":32,"description":60,"image":15,"imageAlt":15,"readingTime":16,"tags":61,"title":66,"updatedAt":67},"69e24b95aa6b273b0ce2c255","no-javascript-fallbacks-in-2026-less-critical-still-necessary","https://seogard.io/blog/no-javascript-fallbacks-in-2026-less-critical-still-necessary","2026-04-17T15:02:45.267Z","Googlebot rend le JS, mais pas toujours complètement. Où les fallbacks no-JS protègent encore votre indexation, vos liens et votre trafic organique.",[62,63,64,65,51],"no-javascript","fallbacks","rendering","indexation","No-JavaScript fallbacks en 2026 : moins critiques, toujours nécessaires","Fri Apr 17 2026 15:02:45 GMT+0000 (Coordinated Universal Time)"]