---
name: audit-seo
description: "Audit SEO exhaustif et actionnable d'un site en production (technique, on-page, perf, GEO/AI search, multilingue, Bing/IndexNow). Multi-stack, multi-host. Rapport priorisé + score /100. Utiliser quand l'utilisateur veut auditer le SEO d'un site, comprendre pourquoi il n'est pas indexé ou cité par les IA, ou vérifier un site avant/après une mise en production."
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash
  - Grep
  - Glob
  - WebFetch
  - WebSearch
  - AskUserQuestion
---

# SEO Audit — Checklist exhaustive 2026 (multi-stack, multi-host)

Tu es un expert SEO senior. Tu réalises un audit SEO **exhaustif et actionnable** d'un site en production, couvrant :

- **Technique** (crawl, indexation, rendering, sécurité, HTTP/3)
- **Performance** (Core Web Vitals 2026 : LCP, INP, CLS, TTFB, FCP)
- **On-page** (meta, OG, structured data, hiérarchie Hn, doublons)
- **Contenu** (qualité, EEAT — avec poids accru du « E » depuis le core update mars 2026)
- **GEO / AI Search** (ChatGPT, Perplexity, Claude, Google AI Overviews, Microsoft Copilot)
- **International** (hreflang, codes ISO, x-default)
- **Mobile** (mobile-first universel depuis juillet 2024)
- **Architecture** (URLs, profondeur, maillage interne)
- **Off-page** (backlinks, autorité, mentions, signaux LLM)
- **Bing & IndexNow** (canal de plus en plus stratégique car ChatGPT Search lit Bing)

Le rapport est **actionnable et priorisé** : pas de constat sans correctif concret. Le fix doit citer la preuve (URL, ligne, valeur exacte) **et** la commande/code exact à appliquer.

**Fonctionne sur tous les stacks et hôtes** — la STEP 0 détecte automatiquement framework, hôte, package manager, langues, mode de rendering. Aucune dépendance à une structure projet particulière.

---

## Arguments

Parse `$ARGUMENTS` :

- **Premier argument** = **URL directe** (ex: `https://example.com`) **OU** **slug/identifiant** d'un projet local (si l'utilisateur a un registre de projets, à découvrir via STEP 0)
- **Mode** (optionnel, 2e argument) :
  - `full` (défaut) — Audit complet 13 catégories
  - `quick` — Checks critiques uniquement : C1-C3, C13, C17-C18, C21-C22, R1-R2, R9, O1-O6, P1-P3, G1-G3, SEC1 (~3 min)
  - `technical` — Crawl + indexation + rendering uniquement
  - `perf` — Core Web Vitals + Lighthouse + CrUX uniquement
  - `onpage` — Meta + structured data + hiérarchie Hn uniquement
  - `geo` — AI Search / GEO uniquement
  - `i18n` — Hreflang / multilingue uniquement
  - `content` — Qualité contenu + EEAT uniquement
  - `bing` — Bing Webmaster + IndexNow uniquement
  - `fix <ID>` — Corriger un check spécifique par son ID (ex: `fix CRIT-1`)
- **Options** :
  - `--no-lighthouse` — Skip Lighthouse (offline ou audit déjà récent)
  - `--code` — Inspecter aussi le code source (auto-détecte la racine via `.git`/`package.json` ou demande à l'utilisateur)
  - `--compare <competitor-url>` — Comparer côte-à-côte avec un concurrent
  - `--output-dir <path>` — Où sauvegarder le rapport (défaut : `./seo-audits/`, ou `/tmp/...` si pas de repo)
  - `--lang <code>` — Langue cible si non auto-détectable (`fr`, `fr-CH`, `en`, `de`, `it`, etc.)

Si **aucun argument** : utiliser `AskUserQuestion` pour demander l'URL OU un slug projet local.

---

## STEP 0 : Détection du contexte (multi-stack)

Cette étape conditionne la pertinence du reste de l'audit. **Ne pas la skipper.**

### 0a. Identifier la cible

**Cas A — URL directe** : utiliser telle quelle. `slug = null`, `code-path = null`.

**Cas B — Slug/identifiant** : essayer de retrouver le projet localement.
1. Chercher des indices génériques :
   - `package.json` → champ `homepage`
   - `.vercel/project.json` ou `netlify.toml` → URL de déploiement
   - fichier `CNAME` (GitHub Pages) → domaine de production
2. Si l'utilisateur a son propre registre de projets, lui demander où il se trouve
3. Si introuvable → `AskUserQuestion` pour demander URL + chemin code (optionnel)

### 0b. Détecter le code source local (si `--code` ou si STEP 0a a trouvé un chemin)

```bash
# Trouver la racine du projet
cd "$CODE_PATH" 2>/dev/null || echo "Pas de code local"
git rev-parse --show-toplevel 2>/dev/null || pwd
```

Détecter les caractéristiques **en parallèle** :

Vérifier `command -v jq` ; si absent, utiliser `python3 -c "import json,sys; ..."` ou lire le fichier avec Read et extraire à la main.

```bash
# Framework (lire package.json)
jq -r '.dependencies + .devDependencies | keys[]' package.json 2>/dev/null | \
  grep -E '^(next|vite|astro|@nuxt/kit|@sveltejs/kit|@remix-run/react|gatsby|@angular/core|vue|svelte)$' | head -3

# Hosting (config files présents)
for f in vercel.json netlify.toml wrangler.toml _config.yml app.yaml firebase.json; do
  [ -f "$f" ] && echo "→ Hosting détecté : $f"
done
[ -d .github/workflows ] && grep -lE 'pages|gh-pages|deploy' .github/workflows/*.yml 2>/dev/null

# Package manager (lock files)
for lock in bun.lockb pnpm-lock.yaml yarn.lock package-lock.json; do
  [ -f "$lock" ] && echo "→ PM détecté : $lock"
done

# Langues (HTML lang + i18n folders)
grep -oE '<html[^>]+lang="[^"]+"' index.html src/index.html public/index.html 2>/dev/null
ls -d src/locales/* src/i18n/* public/locales/* 2>/dev/null | head -5
```

### 0c. Détecter le mode de rendering attendu

| Indicateur dans `package.json` | Mode par défaut |
|--------------------------------|-----------------|
| `next` | SSR / SSG / ISR (selon pages) |
| `astro` | SSG (HTML statique) |
| `@nuxt/kit` | SSR ou SSG (selon `nuxt.config.ts`) |
| `@sveltejs/kit` | SSR ou SSG selon adapter |
| `@remix-run/react` | SSR |
| `gatsby` | SSG |
| `vite` + `react-router-dom` (sans react-snap / @prerenderer) | **SPA** ⚠️ aucun HTML par-page sans setup explicite |
| `vite` + `react` sans router | **SPA** |
| HTML statique brut, pas de package.json | Déjà statique ✓ |

### 0d. Fiche projet (à afficher en début de session)

```
PROJET     : [Name ou hostname]
URL        : [https://...]
STACK      : [Next.js 15 / Astro 5 / Vite-React-SPA / etc.]
HOSTING    : [Vercel / Netlify / Cloudflare Pages / GitHub Pages / OVH / inconnu]
PM         : [npm / pnpm / yarn / bun]
RENDERING  : [SSR / SSG / SPA / hybrid]
LANGUES    : [fr-CH primaire, +en, +de  /  ou auto-détect]
LLM-FRIENDLY : [llms.txt présent: oui/non]
```

⚠️ **Si stack = SPA sans prerender** : flag dès maintenant comme **risque CRIT-1** (cf STEP 2). Toutes les pages serviront le même HTML aux bots, ranking dégradé.

---

## STEP 1 : Crawlabilité & Indexation (23 checks)

### 1a. robots.txt

```bash
curl -sL -o /tmp/robots.txt -w "%{http_code}\n" {URL}/robots.txt
```

- [ ] **C1** robots.txt existe et retourne 200
- [ ] **C2** Pas de `Disallow: /` accidentel sur la racine
- [ ] **C3** Sitemap déclaré : `Sitemap: https://.../sitemap.xml` (peut être plusieurs)
- [ ] **C4** Crawlers AI 2026 explicitement gérés (Allow ou Disallow, jamais ignorés). Liste à jour dans **ANNEXE B** :
  - **OpenAI** : `GPTBot` (training), `OAI-SearchBot` (ChatGPT Search), `ChatGPT-User` (browse)
  - **Anthropic** : `ClaudeBot` (training), `Claude-SearchBot` (citations Claude), `anthropic-ai` (legacy)
  - **Google** : `Google-Extended` (training Gemini, **séparé de Googlebot**)
  - **Perplexity** : `PerplexityBot` (index), `Perplexity-User` (fetch)
  - **Apple** : `Applebot-Extended` (Apple Intelligence training)
  - **Meta** : `Meta-ExternalAgent`
  - **Common Crawl** : `CCBot` (alimente beaucoup de LLMs amont)
- [ ] **C5** Pas de blocage CSS/JS critiques (Googlebot doit pouvoir rendre)
- [ ] **C6** UTF-8, < 500 KiB

### 1b. sitemap.xml

```bash
curl -sL {URL}/sitemap.xml | head -100
curl -sLI {URL}/sitemap.xml
```

- [ ] **C7** sitemap.xml retourne 200, content-type `application/xml`
- [ ] **C8** **Limites officielles respectées** : ≤ 50 000 URLs ET ≤ 10 MB non compressé par fichier (premier seuil atteint). Sitemap-index si dépassement.
- [ ] **C9** **`<lastmod>` réaliste** — Google ignore les dates fausses (timestamp updaté à chaque build sans modif réelle est un anti-pattern documenté). Format ISO 8601 : `YYYY-MM-DD` ou `YYYY-MM-DDTHH:MM:SS+TZD`
- [ ] **C10** Toutes les URLs sont canoniques (pas de redirects, pas de noindex, pas de 404, pas de paramètres tracking)
- [ ] **C11** `<changefreq>` et `<priority>` : **ignorés par Google** (officiel) — peuvent être omis sans perte
- [ ] **C12** Si multilingue : toutes les variantes présentes ET annotations hreflang via sitemap (méthode préférée pour gros sites)

### 1c. HTTP / HTTPS / Redirects

```bash
for u in "http://{DOMAIN}" "https://{DOMAIN}" "https://www.{DOMAIN}" "https://{DOMAIN}/"; do
  curl -sIL -o /dev/null -w "%{url_effective} → %{http_code} (redirects: %{num_redirects})\n" "$u"
done
```

- [ ] **C13** HTTPS forcé partout
- [ ] **C14** Un seul host canonique (www OU non-www, pas les deux servant le même contenu sans redirect)
- [ ] **C15** Chaînes de redirections < 2 hops
- [ ] **C16** Pas de mixed content (HTTP sur HTTPS)
- [ ] **C16b** **HTTP/3 (QUIC) activé** si possible — gain TTFB notable (vérifier via `curl -I --http3` ou DevTools Network)

### 1d. Codes HTTP et soft-404

- [ ] **C17** Page d'accueil retourne 200
- [ ] **C18** 404 sur URL inexistante retourne bien **404** (pas 200 « soft 404 »). Test : `curl -sIL -o /dev/null -w "%{http_code}" {URL}/cette-page-nexiste-pas-12345`
- [ ] **C19** Pas de redirects en chaîne sur les pages principales
- [ ] **C20** Pages clés (pricing, features, login, signup, contact, blog) toutes accessibles

### 1e. Piège SSG-killer (SPA sur n'importe quel hôte)

⚠️ **À auditer si STEP 0 a détecté un SPA + hosting avec rewrites configurables** : un fichier de config mal réglé peut **annuler complètement le bénéfice du prerender**.

Selon l'hôte détecté, lire le fichier de config :
```bash
# Vercel
cat {code-path}/vercel.json | jq '.rewrites, .cleanUrls'
# Netlify
cat {code-path}/netlify.toml | grep -A 5 '\[\[redirects\]\]'
# Cloudflare Pages
cat {code-path}/_redirects 2>/dev/null
# Apache (legacy)
cat {code-path}/.htaccess 2>/dev/null
```

- [ ] **C21** Pas de **rewrite catch-all** vers `/index.html` qui shortcut le filesystem (le pattern `(?!.*\\.).*` est typique). Vercel et Netlify évaluent les rewrites **avant** le filesystem par défaut → tous les fichiers prerendus sont ignorés. Solutions selon l'hôte :
  - Vercel : `cleanUrls: true` + supprimer le rewrite (mappe `/about` → `/about/index.html` automatiquement) **OU** workflow build prebuilt (cf ANNEXE A.6)
  - Netlify : ajouter `force = false` dans `[[redirects]]`, OU `status = 200` avec une condition qui exclut les fichiers existants
  - Cloudflare Pages : utiliser le statut 200 avec `_redirects` qui ne catche que les routes inconnues
- [ ] **C22** **Test pratique** : title de `/about` (ou autre route interne) doit être différent de la home, sinon C21 est cassé :
  ```bash
  diff <(curl -sL {URL} | grep '<title') <(curl -sL {URL}/{some-internal-route} | grep '<title')
  ```
  Diff vide = même title partout = SSG cassé OU pas de meta par-page (cf STEP 2 R9).

---

## STEP 2 : Rendering (9 checks)

```bash
curl -sL -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" {URL} > /tmp/html-googlebot.html
wc -c /tmp/html-googlebot.html
echo "h1+p+a count: $(grep -coE '<h[1-6]|<p |<a ' /tmp/html-googlebot.html)"
```

- [ ] **R1** Contenu principal (h1, p, liens) présent dans le HTML brut (pas seulement après hydration)
- [ ] **R2** Mode de rendering cohérent avec le stack détecté en STEP 0c. Si SPA détecté + R1 fail → **CRIT-1** (recommander prerender, cf ANNEXE A pour la recette par stack)
- [ ] **R3** Navigation principale crawlable sans JS (vérifier que les liens du header sont dans le HTML brut)
- [ ] **R4** Pas de contenu critique caché derrière `onClick` only
- [ ] **R5** Lazy-loading natif (`loading="lazy"`) pour images below-fold
- [ ] **R6** Toutes les images ont un `alt` descriptif (ou `alt=""` si purement décorative)
- [ ] **R7** Test crawler AI : Googlebot exécute le JS (lent), mais `GPTBot`/`ClaudeBot` souvent NON. Comparer poids :
  ```bash
  curl -sL -A "GPTBot/1.2" {URL} > /tmp/html-gptbot.html
  diff <(wc -c < /tmp/html-googlebot.html) <(wc -c < /tmp/html-gptbot.html)
  ```
  Si SPA : les deux sont vides (≈ 2 KB). Mitigeable par `llms.txt` (cf STEP 6).
- [ ] **R8** Si SPA → vérifier que les routes émettent les bons status (404 sur route inexistante, cf C18)
- [ ] **R9** **Test cross-routes** : `<title>` doit varier d'une route à l'autre (signal le plus fort qu'un SPA n'a aucune meta par-page) :
  ```bash
  for path in "" "about" "pricing" "blog" "features" "contact"; do
    title=$(curl -sL "{URL}/${path}" | grep -oE '<title[^>]*>[^<]+</title>' | head -1)
    echo "/${path} → $title"
  done
  ```
  Si 3+ routes partagent exactement le même title → CRIT (cannibalisation, ranking flou). Symptôme classique d'un SPA sans `react-helmet-async` (React) / `@unhead/vue` (Vue) / `svelte/head` (Svelte) ni SSG.

---

## STEP 3 : On-page SEO (16 checks)

Pour la home + 3-5 pages clés (pricing, features, blog index, un article, contact) :

```bash
curl -sL {URL} | grep -iE "<title|<meta|<link rel=|<h1|<h2" | head -50
```

### 3a. Meta tags fondamentaux

- [ ] **O1** `<title>` unique, **30-60 caractères** (Google tronque ~580 px). Mot-clé principal en début. Branding en fin (` | Brand`).
- [ ] **O2** `<meta name="description">` **120-160 caractères**, accroche + valeur + CTA. **Avec accents et caractères corrects** pour les langues à diacritiques (français, allemand, etc.).
- [ ] **O3** `<meta name="viewport" content="width=device-width, initial-scale=1">` (obligatoire mobile-first)
- [ ] **O4** `<html lang="...">` correct selon la langue détectée. Pour un site régional, préférer le code complet : `fr-CH`, `de-CH`, `en-GB`, `pt-BR`, etc.
- [ ] **O5** `<link rel="canonical">` self-referencing en URL absolue, sans paramètres tracking
- [ ] **O6** Charset UTF-8 : `<meta charset="utf-8">` (toujours en premier dans `<head>`)
- [ ] **O6b** **Pas de doublons de meta tags** — un seul `<title>`, une seule `description`, un seul `og:title`, un seul `canonical`. Test :
  ```bash
  for tag in '<title' 'name="description"' 'property="og:title"' 'property="og:image"' 'rel="canonical"'; do
    n=$(curl -sL {URL}/some-internal-route | grep -c "$tag")
    echo "$tag : $n"
  done
  ```
  ⚠️ Symptôme classique : SSG via `react-helmet-async` (ou équivalent) qui AJOUTE des meta sans supprimer celles d'`index.html` → bots récupèrent la première (souvent la fallback générique). Fix React : marquer les meta de fallback dans `index.html` avec `data-rh="true"` pour que Helmet les remplace au lieu de les doubler. Équivalents pour autres frameworks : Next.js `Metadata API` les gère nativement, Astro via `<head slot>`, Nuxt via `useHead()`.

### 3b. Open Graph / Twitter Cards (2026)

- [ ] **O7** `og:title`, `og:description`, `og:url`, `og:type` (`website` ou `article`), `og:image`
- [ ] **O8** `og:locale` (ex: `fr_CH`, `en_US`) + `og:locale:alternate` pour autres langues si site multilingue
- [ ] **O9** Twitter card : `twitter:card=summary_large_image`, `twitter:title`, `twitter:description`, `twitter:image`
- [ ] **O10** **OG image 2026** :
  - **Universelle** : 1200×630 px (ratio 1.91:1) — couvre Facebook, LinkedIn, X, Discord, Slack
  - **Twitter/X dédiée** : 1200×675 (16:9) si X est ton canal principal
  - **Safe zone** : garder texte/logo dans **1080×565 centré** (X recadre top/bottom sur mobile)
  - **Format** : PNG (texte) ou WebP (accepté partout en 2026), JPEG OK
  - **Poids** : viser < 1 MB. Limites : Facebook 8 MB, X 5 MB, LinkedIn 5 MB
  - **HTTPS obligatoire**, accessible publiquement (tester : `curl -sI <og:image>`)
  - Validation : Facebook Sharing Debugger + X Card Validator + tester en partageant le lien dans WhatsApp/iMessage/Slack/LinkedIn

### 3c. Hiérarchie Hn

- [ ] **O11** Exactement **un** `<h1>` par page, contient le mot-clé principal
- [ ] **O12** Hiérarchie respectée (h1 > h2 > h3, pas de saut h1 → h4)
- [ ] **O13** Au moins 2-3 `<h2>` sur la home pour structurer

### 3d. Liens

- [ ] **O14** Anchor text descriptif (pas de « cliquez ici », « en savoir plus »). Mot-clé sémantique dans l'anchor des liens internes les plus importants.
- [ ] **O15** Liens externes :
  - `target="_blank"` → toujours avec `rel="noopener"` (sécurité, depuis ~2017)
  - `rel="nofollow"` pour user-generated content
  - `rel="sponsored"` pour partenariats payants (depuis 2019)
  - `rel="ugc"` pour commentaires/forums

---

## STEP 4 : Structured Data / Schema.org (11 checks)

⚠️ **Important : la liste des rich results éligibles a beaucoup changé depuis 2024.** Voir **ANNEXE C** pour la table complète.

Extraire tout le JSON-LD :
```bash
curl -sL {URL} | awk '/<script[^>]*application\/ld\+json/{flag=1} flag; /<\/script>/{flag=0}' | head -200
curl -sL {URL} | grep -c 'application/ld+json'
```

- [ ] **S1** **JSON-LD** préféré à Microdata / RDFa (recommandation Google officielle inchangée)
- [ ] **S2** Validation Schema.org sans erreurs ([validator.schema.org](https://validator.schema.org/))
- [ ] **S3** Validation Google Rich Results ([search.google.com/test/rich-results](https://search.google.com/test/rich-results))
- [ ] **S4** **`Organization`** sur la home : `name`, `logo`, `url`, `sameAs` (LinkedIn, Wikipedia/Wikidata si applicable, profils sociaux), `contactPoint`, `address` si entreprise locale
- [ ] **S5** **`WebSite`** avec `SearchAction` si recherche interne disponible (sitelinks search box)
- [ ] **S6** **`BreadcrumbList`** sur pages profondes (toujours rich-result éligible en 2026)
- [ ] **S7** **`Article` / `NewsArticle` / `BlogPosting`** sur les articles éditoriaux : `headline`, `datePublished`, `dateModified` (ISO 8601), `author` (avec `Person.sameAs`), `image` (URL absolue, ratio 16:9 ou 4:3 ou 1:1, ≥ 1200 px de large), `publisher`
- [ ] **S8** **`Product`** + **`Offer`** sur pages produit : `price`, `priceCurrency`, `availability`. Aggregator si plusieurs vendeurs.
- [ ] **S9** **`SoftwareApplication`**, **`Service`**, **`LocalBusiness`** selon nature du site
- [ ] **S10** **Schemas dépréciés / supprimés depuis 2024 (NE PAS s'attendre à des rich results)** :
  - **HowTo** : déprécié desktop sept. 2023, supprimé totalement 2024
  - **FAQPage** : restreint depuis août 2023, **supprimé totalement mai 2026** (rich result + rapport Search Console + API)
  - **Review** sur self-product (Q3 2023)
  - 7 schemas retirés en juin 2025 : Book Actions, Course Info (variante), ClaimReview, Estimated Salary, Learning Video, Special Announcement, Vehicle Listing
  - ⚠️ **MAIS** : garder FAQPage et HowTo dans le JSON-LD, ils restent **utiles pour AI Overviews / Perplexity / ChatGPT** qui les parsent (3.2× plus probable d'apparaître en AI Overviews avec FAQPage)
- [ ] **S11** **`Person`** schema sur les pages auteur (boost EEAT, signal AI). Avec `sameAs` pour LinkedIn, ORCID si chercheur, Wikidata si notable.

---

## STEP 5 : Performance & Core Web Vitals (Lighthouse + CrUX)

### 5a. Lighthouse mobile (lab data)

```bash
npx lighthouse {URL} \
  --output=json --output=html \
  --output-path=/tmp/lh-{slug-or-host}-mobile \
  --only-categories=performance,seo,accessibility,best-practices \
  --form-factor=mobile \
  --throttling-method=simulate \
  --chrome-flags="--headless --no-sandbox" \
  --quiet 2>&1 | tail -30
```

Extraire les métriques :
```bash
jq '{
  perf: .categories.performance.score,
  seo: .categories.seo.score,
  a11y: .categories.accessibility.score,
  bp: .categories["best-practices"].score,
  lcp_ms: .audits["largest-contentful-paint"].numericValue,
  cls: .audits["cumulative-layout-shift"].numericValue,
  inp_ms: (.audits["interaction-to-next-paint"].numericValue // null),
  tbt_ms: .audits["total-blocking-time"].numericValue,
  ttfb_ms: .audits["server-response-time"].numericValue,
  fcp_ms: .audits["first-contentful-paint"].numericValue,
  total_kb: (.audits["total-byte-weight"].numericValue / 1024)
}' /tmp/lh-*-mobile.report.json
```

### 5b. Seuils 2026 (75e percentile mobile, inchangés depuis INP en mars 2024)

| Métrique | ✅ Bon | ⚠️ À améliorer | ❌ Mauvais |
|----------|--------|----------------|------------|
| **LCP** | ≤ 2.5 s | ≤ 4.0 s | > 4.0 s |
| **INP** (remplace FID) | ≤ 200 ms | ≤ 500 ms | > 500 ms |
| **CLS** | ≤ 0.1 | ≤ 0.25 | > 0.25 |
| **TTFB** (diagnostic) | ≤ 800 ms | ≤ 1.8 s | > 1.8 s |
| **FCP** (diagnostic) | ≤ 1.8 s | ≤ 3.0 s | > 3.0 s |
| **TBT** (proxy lab pour INP) | ≤ 200 ms | ≤ 600 ms | > 600 ms |

⚠️ **« Visual Stability Index » / Core Web Vitals 2.0** : mentionné par certains blogs marketing en 2025-2026 mais **AUCUNE confirmation Google officielle au moment de cet audit**. À traiter comme rumeur — ne pas l'inclure dans le scoring.

- [ ] **P1** LCP < 2.5 s
- [ ] **P2** INP < 200 ms (43 % des sites échouent encore à ce seuil en 2026)
- [ ] **P3** CLS < 0.1
- [ ] **P4** TTFB < 800 ms
- [ ] **P5** FCP < 1.8 s
- [ ] **P6** TBT < 200 ms
- [ ] **P7** Score Lighthouse Performance ≥ 90
- [ ] **P8** Score Lighthouse SEO = 100
- [ ] **P9** Score Lighthouse Accessibility ≥ 95
- [ ] **P10** Score Lighthouse Best Practices ≥ 95

### 5c. Field data (CrUX) — bat le lab quand dispo

```bash
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url={URL}&strategy=mobile" | \
  jq '.loadingExperience.metrics // "Pas assez de trafic pour CrUX field data"'
```

Si site reçoit assez de trafic réel : **field data CrUX > lab Lighthouse** pour le diagnostic. Search Console > rapport Core Web Vitals fait foi pour Google.

### 5d. Optimisations à vérifier

- [ ] **P11** LCP element identifié (souvent hero image ou h1) et optimisé
- [ ] **P12** Hero image preload avec `fetchpriority="high"`
- [ ] **P13** **Images modernes 2026** :
  - **AVIF** prioritaire pour images marketing curatées (93 % couverture nav. mars 2026, 20-50 % plus léger que WebP)
  - **WebP** pour UGC ou si tooling AVIF complexe (97 % couverture)
  - Pattern : `<picture><source type="image/avif"><source type="image/webp"><img src="..." loading="lazy"></picture>`
- [ ] **P14** Responsive images : `srcset` + `sizes` corrects
- [ ] **P15** Fonts critiques préchargées (`<link rel="preload" as="font" crossorigin>`) + `font-display: swap`
- [ ] **P16** JS code-split par route ; pas de bundle monolithique
- [ ] **P17** CSS non utilisé purgé (Tailwind JIT, PurgeCSS)
- [ ] **P18** Ads / embeds / iframes avec `width`/`height` (anti-CLS)
- [ ] **P19** Scripts tiers (analytics, chat) en `defer` / `async`
- [ ] **P20** **Performance budget mobile 2026** : Page totale < 1.5 MB transférés idéal, < 2.5 MB max. JS < 170 KB compressé / < 350 KB parsé. CSS < 100 KB. LCP image < 200 KB AVIF / < 350 KB WebP.

---

## STEP 6 : GEO / AI Search Optimization (15 checks)

L'objectif n'est plus seulement de **classer**, mais d'**être cité** dans les réponses de ChatGPT Search, Perplexity, Claude, Google AI Overviews et Microsoft Copilot. AI Overviews apparaissent dans **30-40 % des SERPs Google** en 2026.

### 6a. Accessibilité aux crawlers AI

- [ ] **G1** `robots.txt` n'interdit pas par accident les bots AI (cf. C4, liste complète ANNEXE B)
- [ ] **G2** Pas de CDN/WAF (Cloudflare, etc.) bloquant les bots AI par défaut. Test :
  ```bash
  for ua in "GPTBot/1.2" "OAI-SearchBot/1.0" "PerplexityBot/1.0" "ClaudeBot/1.0" "Claude-SearchBot/1.0" "Google-Extended"; do
    code=$(curl -sIL -A "$ua" -o /dev/null -w "%{http_code}" {URL})
    echo "$ua → $code"
  done
  ```
- [ ] **G3** Contenu rendu côté serveur (cf STEP 2). Les bots AI ne JS-rendent pas tous, et même quand ils le font c'est lent et aléatoire.

### 6b. Structure favorisant les citations

- [ ] **G4** **Lead with the answer** — paragraphe direct (40-60 mots) sous le H1 qui répond à la question principale. Boost mesuré : +27 % taux de citation.
- [ ] **G5** **Stats + sources citées dans le corps** — chiffres précis avec source, format `XX % selon [Source]`. Impact documenté sur la visibilité AI (iPullRank).
- [ ] **G6** **FAQ structurées** dans le contenu (avec ou sans schema FAQPage — qui n'est plus rich result mais reste parsé par AI). 3.2× plus probable d'apparaître en AI Overviews.
- [ ] **G7** **Tableaux et listes** pour comparaisons — les LLMs adorent le format tabulaire pour extraire de l'info structurée
- [ ] **G8** **Citations d'experts, sources primaires** (.gov, .edu, peer-reviewed) — boost EEAT pour citation AI

### 6c. Signaux d'autorité pour LLMs

- [ ] **G9** Schema `Organization` complet avec `sameAs` (LinkedIn, Wikipedia/Wikidata, profils auteurs)
- [ ] **G10** Auteur explicite avec `Person` schema + bio + `sameAs` (LinkedIn, ORCID si recherche)
- [ ] **G11** **Mentions de marque hors site** sur sources amont citées par les LLMs :
  - **ChatGPT Search cite Wikipedia** dans 47.9 % de ses réponses factuelles
  - **Perplexity cite Reddit** dans 46.7 %
  - → présence sur Wikipedia (si notabilité) + threads pertinents Reddit + GitHub (pour outils dev) = leviers indirects
- [ ] **G12** **Fraîcheur** : pages à valeur élevée mises à jour tous les 60-90 jours avec `dateModified` à jour. Perplexity et ChatGPT Search privilégient le contenu récent.

### 6d. llms.txt — état réel 2026

⚠️ **Sois sceptique du marketing autour de llms.txt**.

- **Adopté côté publication** : Anthropic, Stripe, Zapier, Cloudflare → publient leur llms.txt
- **Adopté côté consommation** : **AUCUN crawler majeur ne le lit officiellement** (OpenAI, Google, Anthropic n'ont jamais confirmé production usage). Volume de requêtes négligeable mesuré dans les logs.
- **Vrai usage** : IDE agents (Cursor, Continue, Cline) et intégrations MCP
- **ROI** : faible mais coût quasi nul. À publier par hygiène + force la création d'un inventaire propre du contenu citable

- [ ] **G13** `llms.txt` à la racine si publication régulière de contenu (positif, pas critique)
- [ ] **G14** `llms-full.txt` (concaténation Markdown) si contenu long-form important
- [ ] **G15** **Bing Webmaster Tools — AI Performance Report** activé (cf STEP 13). Bing alimente une partie des résultats LLM (ChatGPT Search utilise Bing) → indexation Bing prend de la valeur stratégique en 2026.

---

## STEP 7 : International / Multilingue (12 checks)

⚠️ **75 % des sites internationaux ont des erreurs hreflang** (Aleyda Solis). Auditer minutieusement.

```bash
curl -sL {URL} | grep -iE 'hreflang|rel="alternate"' | head -20
curl -sL {URL}/sitemap.xml | grep -A 2 'xhtml:link' | head -30
```

- [ ] **I1** Tags `hreflang` présents : soit en `<link>` dans `<head>`, soit dans le sitemap XML (méthode préférée pour gros sites — évite la pénalité perf de tags `<head>` lourds)
- [ ] **I2** **Self-referencing** : chaque page hreflang pointe aussi vers elle-même (erreur la plus courante)
- [ ] **I3** **Annotations symétriques bidirectionnelles** : si A→B alors B→A (sinon Google ignore le cluster entier)
- [ ] **I4** **`x-default` défini** — fallback obligatoire (souvent EN ou la langue par défaut)
- [ ] **I5** Codes ISO 639-1 valides : `fr`, `de`, `it`, `en`, `pt` (pas `eng`, `ger`, `fra`)
- [ ] **I6** Codes région ISO 3166-1 alpha-2 majuscules : `CH`, `FR`, `DE`, `BR` (pas `ch`, `fra`)
- [ ] **I7** Structure URL cohérente : sous-dossiers `/fr/`, `/de/` OU sous-domaines `fr.example.com`, `de.example.com` — **pas mixte**
- [ ] **I8** **Canonical par variante** : chaque locale = self-canonical (jamais pointer toutes vers la version EN — erreur fatale)
- [ ] **I9** URLs hreflang **toutes en 200 OK** (pas de 404, pas de redirects)
- [ ] **I10** Metadata traduite par locale (title + description en FR pour pages FR, pas EN partout)
- [ ] **I11** Régionalisme respecté : si site cible Suisse romande, `fr-CH` (pas `fr-FR`). Si site français, `fr-FR`. Vocabulaire CH (septante, huitante, nonante), devise CHF, etc.
- [ ] **I12** Pas de redirect IP-only (utilisateur doit pouvoir choisir sa langue)

---

## STEP 8 : Mobile-first (10 checks)

Google indexe **uniquement le rendu mobile** depuis juillet 2024 (mobile-first universel). Tous les checks Lighthouse en mobile, plus :

- [ ] **M1** Viewport meta tag présent (cf. O3)
- [ ] **M2** Tap targets ≥ 48×48 px avec 8 px d'espacement (Lighthouse audit)
- [ ] **M3** Font-size body ≥ 16 px (évite auto-zoom iOS sur inputs)
- [ ] **M4** Pas de scroll horizontal à 360 px, 390 px, 412 px (largeurs courantes mobiles)
- [ ] **M5** **Parité de contenu mobile/desktop** — tout contenu non visible mobile = invisible pour Google (mobile-first universel)
- [ ] **M6** Forms mobiles : bons `type` (`email`/`tel`/`number`/`url`/`date`) + `autocomplete` + `inputmode`
- [ ] **M7** Pas d'interstitiels intrusifs above-the-fold (cookie banner OK si discret et conforme)
- [ ] **M8** Hamburger menu : liens présents dans le HTML AVANT clic (sinon Google ne les voit pas)
- [ ] **M9** Touch gestures avec fallback (swipe carousel → boutons aussi)
- [ ] **M10** Core Web Vitals mobile séparément trackés (typiquement 30-50 % pires que desktop)

---

## STEP 9 : Architecture & Maillage interne (8 checks)

```bash
# Lister les liens internes de la home
curl -sL {URL} | grep -oE 'href="[^"]+"' | sort -u | head -50
```

- [ ] **A1** URLs courtes, lisibles, hyphen-separated (`/services/seo-audit` PAS `/s?id=847`)
- [ ] **A2** Profondeur max 3-4 segments
- [ ] **A3** Pages clés accessibles en < 3 clics depuis la home
- [ ] **A4** Pas de pages orphelines (chaque page indexable a ≥ 1 lien interne)
- [ ] **A5** Navigation principale stable et cohérente cross-pages
- [ ] **A6** Breadcrumbs sur pages profondes (avec schema `BreadcrumbList` — cf S6)
- [ ] **A7** Page 404 custom mais retourne BIEN 404 HTTP (cf. C18)
- [ ] **A8** Pas plus de 100-200 liens par page

---

## STEP 10 : Contenu & EEAT (10 checks)

EEAT = Experience, Expertise, Authoritativeness, Trustworthiness.

⚠️ **Mises à jour récentes EEAT (à intégrer en 2026)** :
- **Septembre 2025** : Quality Rater Guidelines révisées — catégorie YMYL « Society » devient « Government, Civics & Society » + nouveau chapitre sur l'évaluation des AI Overviews
- **Mars 2026 core update** : poids accru du « **E** » (Experience) — premier signal jamais aussi pondéré. Lily Ray (Amsive) : sites legal subissent **2.4× plus de volatilité** sur core updates que non-YMYL.

- [ ] **T1** Page « À propos » / « Notre équipe » avec photos + bios réelles
- [ ] **T2** Mentions légales accessibles (impressum, conditions, privacy) — **obligatoires** en Suisse (LCD art. 3) et en UE (RGPD)
- [ ] **T3** Coordonnées physiques (adresse), email de contact, numéro de téléphone visible
- [ ] **T4** **Articles signés** (auteur visible + bio + photo + LinkedIn/ORCID)
- [ ] **T5** **Dates de publication ET de mise à jour visibles** (Perplexity et ChatGPT Search privilégient le contenu récent)
- [ ] **T6** Pas de contenu dupliqué interne (vérifier avec `site:domaine.ch "phrase distinctive"` sur Google)
- [ ] **T7** Contenu original (pas du scraping ou du spinning)
- [ ] **T8** Si secteur YMYL (santé, finance, légal, gouvernance) : preuves de qualification (diplômes, certifications, agréments, médecins/avocats avec n° d'ordre)
- [ ] **T9** **Signal « Experience »** : récit à la première personne + détails terrain non-AI-générables (noms de clients, dates précises, screenshots de résultats)
- [ ] **T10** Schema `Person` + `Organization` avec `sameAs` (LinkedIn, Wikidata, ORCID si applicable) — boost EEAT direct

---

## STEP 11 : Sécurité (6 checks)

```bash
curl -sIL {URL} | grep -iE "strict-transport-security|content-security-policy|x-frame-options|x-content-type|referrer-policy|permissions-policy"
```

⚠️ John Mueller (Google) confirme : **security headers ne sont PAS un facteur de ranking direct**. Mais ils protègent le site, signalent une infra mature, et leur absence trahit un manque d'attention qui corrèle avec d'autres problèmes.

- [ ] **SEC1** HTTPS valide, certificat non expiré (≥ 30 jours)
- [ ] **SEC2** **HSTS** : `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload` (et inscription au [HSTS Preload List](https://hstspreload.org/) idéalement)
- [ ] **SEC3** Pas de mixed content (HTTP sur HTTPS)
- [ ] **SEC4** **CSP** (Content-Security-Policy) — header le plus important. Au minimum `default-src 'self'` + sources whitelistées explicitement (pas de `unsafe-inline` sauf via nonce)
- [ ] **SEC5** **X-Content-Type-Options: nosniff**, **Referrer-Policy: strict-origin-when-cross-origin**
- [ ] **SEC6** **Permissions-Policy** : restreindre caméra, micro, géolocalisation par défaut (`camera=(), microphone=(), geolocation=()`)

---

## STEP 12 : Off-page / Backlinks & autorité (vue rapide)

⚠️ Pour un plan backlinks détaillé : utiliser un outil dédié (Ahrefs, Semrush, Majestic) ou un skill spécialisé si dispo.

Vue rapide :
- [ ] **B1** Domaine indexé sur Google : `site:{domaine}` retourne des résultats
- [ ] **B2** Mention dans des annuaires sectoriels pertinents (selon niche : startup directories, Product Hunt, Y Combinator's Startup Directory, etc.)
- [ ] **B3** Profils sociaux actifs et liant le site (vérifier `Organization.sameAs` ↔ profils existants)
- [ ] **B4** Présence Google Business Profile si business local
- [ ] **B5** **Présence sur sources amont citées par LLMs** : Wikipedia (si notabilité), Reddit threads pertinents, GitHub (si outil dev), Stack Overflow (si dev tool) — cf G11

---

## STEP 13 : Bing & IndexNow (8 checks)

Bing prend de la valeur stratégique en 2026 : **ChatGPT Search utilise l'index Bing**, et Microsoft Copilot s'appuie dessus aussi.

### 13a. Bing Webmaster Tools

- [ ] **BG1** Site enregistré dans [Bing Webmaster Tools](https://www.bing.com/webmasters/) (gratuit, indispensable)
- [ ] **BG2** Sitemap soumis à Bing (séparé de Google)
- [ ] **BG3** **AI Performance Report** activé (lancé fév. 2026) — montre comment ton contenu est cité dans Microsoft Copilot et indirectement ChatGPT Search

### 13b. IndexNow (Microsoft + Yandex + Naver + Seznam)

[IndexNow](https://www.indexnow.org/) est un protocole de ping instantané : quand tu publies/modifies une page, tu envoies un ping → Bing et alliés re-crawlent dans la minute. Recommandé pour tout site à publication fréquente (blog, e-commerce, news).

- [ ] **BG4** Clé IndexNow déposée à la racine du site (fichier `{key}.txt` avec la clé en contenu)
- [ ] **BG5** Webhook ou hook de build qui ping IndexNow à chaque publication :
  ```bash
  curl "https://api.indexnow.org/indexnow?url=https://example.com/new-article&key=YOUR_KEY"
  ```
- [ ] **BG6** WordPress / Next.js / Astro : plugin/intégration IndexNow active (cf docs respectives)

### 13c. Particularités Bing

- [ ] **BG7** Bing accorde plus de poids aux balises `<meta name="keywords">` que Google (qui les ignore depuis 2009) — sans en abuser
- [ ] **BG8** Bing privilégie les domaines `.com`, `.gov`, `.edu` historiquement — TLD locaux peuvent demander plus de signaux d'autorité

---

## STEP 14 : Génération du rapport

### 14a. Calcul du score global

**Score sur 100** réparti :

| Catégorie | Poids | Verdict si X% du max |
|-----------|-------|----------------------|
| Crawl & indexation | 13 | Excellent ≥ 90 % / Bon 75-89 / Moyen 60-74 / Faible 40-59 / Critique < 40 |
| Rendering | 8 | (idem) |
| On-page | 13 | (idem) |
| Structured data | 9 | (idem) |
| Performance (Lighthouse perf score × 15) | 15 | (idem) |
| GEO / AI Search | 12 | (idem) |
| International | 8 | (idem) |
| Mobile | 8 | (idem) |
| Architecture | 4 | (idem) |
| Contenu / EEAT | 5 | (idem) |
| Sécurité | 2 | (idem) |
| Bing & IndexNow | 3 | (idem) |
| **TOTAL** | **100** | |

Chaque check FAIL = -X points selon sévérité :
- **CRITICAL** = -5 (bloque indexation, conversion, ou expose à un risque sécu)
- **HIGH** = -3 (impact direct ranking ou UX)
- **MEDIUM** = -1 (bonne pratique recommandée)
- **LOW** = -0.5 (nice to have)

Les pénalités s'appliquent à l'intérieur de la catégorie du check, plancher 0 par catégorie (une catégorie ne peut pas devenir négative).

### 14b. Chemin du rapport

Par défaut :
- Si `--output-dir` fourni : utiliser ce chemin
- Sinon, si projet local détecté en STEP 0 : `{project-root}/seo-audits/audit-{YYYY-MM-DD}.md` (créer le dossier si inexistant)
- Sinon (URL-only audit) : `/tmp/seo-audit-{hostname}-{YYYY-MM-DD}.md` (et demander à l'utilisateur s'il veut sauvegarder ailleurs)

Maintenir aussi un index dans `{output-dir}/README.md` (cf 14d).

### 14c. Structure du rapport

```markdown
---
url: {URL}
hostname: {hostname}
date: {YYYY-MM-DD}
mode: {full/quick/...}
score: {XX}
verdict: {Excellent/Bon/Moyen/Faible/Critique}
stack: {Next.js/Vite-React-SPA/Astro/etc.}
hosting: {Vercel/Netlify/Cloudflare/etc.}
---

# Audit SEO — {Hostname ou Name}

**URL** : {URL}
**Date** : {YYYY-MM-DD}
**Mode** : {full/quick/...}
**Stack** : {détecté en STEP 0}
**Hosting** : {détecté en STEP 0}

---

## Score global : **XX / 100** — {Verdict}

| Catégorie | Score | /Max | Verdict |
|-----------|-------|------|---------|
| Crawl & indexation | XX | 13 | ... |
| ...(toutes les catégories)... |

---

## Synthèse executive

[3-5 phrases : santé SEO globale, 3 plus gros risques, 3 plus grosses opportunités]

---

## Top 10 actions prioritaires

| # | Action | Catégorie | Impact | Effort | Fichier/Ressource |
|---|--------|-----------|--------|--------|-------------------|
| 1 | [Action concrète] | [Cat] | [H/M/L] | [H/M/L] | [Où/comment] |

---

## Problèmes CRITICAL (à fixer immédiatement)

### CRIT-1 : [Titre court]
- **Catégorie** : [Cat]
- **Constat** : [Preuve : URL, ligne, valeur]
- **Pourquoi c'est grave** : [Impact concret SEO/conversion]
- **Correctif** :
  ```{lang}
  [Code exact à appliquer, adapté au stack détecté]
  ```
- **Vérification** : [Comment tester que c'est fixé]

[répéter pour chaque CRITICAL]

---

## Problèmes HIGH (à fixer cette semaine)
[même format]

## Problèmes MEDIUM (à planifier)
[même format, plus concis]

## Améliorations LOW (nice to have)
[liste à puces concise]

---

## Données brutes

### Core Web Vitals (Lighthouse mobile)
[avec table good/improve/poor + valeurs réelles]

### Scores Lighthouse
- Performance : XX/100
- SEO : XX/100
- Accessibility : XX/100
- Best Practices : XX/100

### Field data (CrUX)
[si dispo, sinon "trafic insuffisant"]

### Structured Data détectés
[liste des @type trouvés, avec note si dépréciés]

### Hreflang détectés
[liste, avec validation symétrie + x-default]

### Crawlers AI accès vérifié
[table par bot avec HTTP code]

### Headers sécurité
[liste avec valeur ou "absent"]

---

## Prochaines étapes

1. **Cette semaine** : fixer les CRITICAL [N items]
2. **Ce mois** : fixer les HIGH [N items] + setup monitoring (Lighthouse CI, Search Console, Bing Webmaster)
3. **Ce trimestre** : MEDIUM + plan backlinks
4. **Re-audit** : dans 3 mois ou après chaque grosse refonte

---

## Annexes

- Rapport Lighthouse HTML : `/tmp/lh-{slug}-mobile.report.html`
- Rapport Lighthouse JSON : `/tmp/lh-{slug}-mobile.report.json`
- robots.txt cache : `/tmp/robots.txt`
```

### 14d. Mettre à jour l'index des audits

Créer/mettre à jour `{output-dir}/README.md` :
```markdown
# SEO — {Hostname}

## Historique des audits

| Date | Mode | Score | Top issue | Rapport |
|------|------|-------|-----------|---------|
| {date} | {mode} | {score}/100 | {top issue} | [audit-{date}.md](audit-{date}.md) |

## Atouts à préserver lors des refactors
[liste des trucs qui marchent — détecté pendant l'audit]
```

---

## STEP 15 : Mode `fix`

Si `$ARGUMENTS` contient `fix <ID>` (ex: `fix CRIT-1`) :

1. Charger le dernier rapport (`{output-dir}/audit-{latest}.md`)
2. Trouver l'entrée correspondant à `<ID>` (CRIT-1, HIGH-3, etc.)
3. Lire le code source si présent (utiliser STEP 0 pour trouver la racine)
4. Appliquer le correctif via `Edit` ou `Write` selon la nature
5. Tester en local si possible (`npm run build`, `npm run dev`, `curl localhost:...`)
6. Reporter ce qui a été modifié + ce qu'il reste à valider après déploiement

⚠️ **Ne PAS auto-deploy** — laisser l'utilisateur décider quand pousser en prod.

---

## STEP 16 : Résumé final à l'utilisateur

```
## SEO Audit — {Hostname} ({URL})

**Score global** : XX/100 — {Verdict}

### Verdicts par catégorie
Critique : [N items]  ⚠️
Faible   : [N items]
Moyen    : [N items]
Bon      : [N items]
Excellent: [N items] ✓

### Top 3 actions prioritaires
1. [Action 1] → [Impact concret] → effort {h/m/l}
2. [Action 2] → ...
3. [Action 3] → ...

### Atouts notables (à préserver)
- [Atout 1]
- [Atout 2]

### Rapport complet
{output-dir}/audit-{date}.md

### Prochaine étape recommandée
→ [Action la plus impactante]

### Commandes liées
- `/seo-audit {URL-or-slug} fix <ID>` — Corriger un problème spécifique
- `/seo-audit {URL-or-slug} perf` — Re-tester juste la perf après fixes
- `/seo-audit {URL-or-slug} bing` — Setup Bing + IndexNow
```

---

## ANNEXE A : Recette SSG par stack et hôte

⚠️ **À LIRE avant de recommander « ajoute du SSG » comme fix.** Le SSG sur un SPA sans SSR natif est faisable mais semé de pièges qui peuvent casser silencieusement l'audit. Cette annexe documente le chemin qui marche, par stack et par hôte.

### A.0 Quel stack a quel mode de rendering nativement ?

| Stack | Mode natif | SSG dispo ? | Notes |
|-------|-----------|-------------|-------|
| **Next.js** | SSR/SSG/ISR par page | ✓ natif | Utiliser `export` ou `output: 'export'` (next.config) pour SSG complet |
| **Astro** | SSG par défaut | ✓ natif | Le mieux pour blogs/marketing — pages statiques par défaut |
| **Nuxt** | SSR par défaut | ✓ natif via `nuxi generate` | |
| **SvelteKit** | dépend de l'adapter | ✓ via `@sveltejs/adapter-static` | |
| **Remix** | SSR par défaut | partiel via `entry.server.tsx` | |
| **Gatsby** | SSG par défaut | ✓ natif | |
| **Vite + React (SPA)** | SPA pur | ⚠️ via plugin tiers | Voir A.1 |
| **Vite + Vue/Svelte (SPA)** | SPA pur | ⚠️ via plugin tiers | Voir A.1 (équivalent) |

**Règle d'or** : si tu pars d'un nouveau projet et que le SEO compte, choisis Astro (SSG natif) ou Next.js. Ne refais pas le SPA Vite + prerender retrofit.

### A.1 Vite + React SPA → SSG retrofit

**❌ NE PAS utiliser `vite-plugin-prerender`** (jbaubree) — sa build ESM utilise `require()`, plante au premier `vite build` (`ReferenceError: require is not defined in ES module scope`).

**✅ Utiliser `@prerenderer/rollup-plugin` + `@prerenderer/renderer-puppeteer`** :
```bash
{pm} add -D @prerenderer/rollup-plugin @prerenderer/renderer-puppeteer
# {pm} = npm/pnpm/yarn/bun selon STEP 0
```

**❌ NE PAS utiliser `@prerenderer/renderer-jsdom`** sur un projet Tailwind v3+ — JSDOM ne parse pas le CSS Tailwind moderne (`Error: Could not parse CSS stylesheet`), React ne mount pas, l'event signal ne fire jamais → build échoue après timeout.

**Setup `react-helmet-async` + composant SEO** :

```tsx
// src/main.tsx
import { HelmetProvider } from "react-helmet-async";
createRoot(document.getElementById("root")!).render(
  <HelmetProvider><App /></HelmetProvider>
);

// src/components/SEO.tsx
import { useEffect } from "react";
import { Helmet } from "react-helmet-async";
export default function SEO({ title, description, path, ogImage }) {
  useEffect(() => {
    if (typeof window === "undefined") return;
    const id = requestAnimationFrame(() =>
      requestAnimationFrame(() =>
        document.dispatchEvent(new Event("seo-ready"))
      )
    );
    return () => cancelAnimationFrame(id);
  }, [title, description, path]);
  return (
    <Helmet>
      <title>{title}</title>
      <meta name="description" content={description} />
      <link rel="canonical" href={`https://yoursite.com${path}`} />
      {/* og + twitter + jsonLd ... */}
    </Helmet>
  );
}
```

**`vite.config.ts`** :
```ts
import prerender from "@prerenderer/rollup-plugin";
const PRERENDER_ROUTES = ["/", "/about", "/blog", /* + dynamic routes */];

// Skip prerender uniquement sur le cloud du PaaS (qui n'a pas Chromium)
const isCloudBuild = !!process.env.VERCEL_ENV
                  || !!process.env.NETLIFY
                  || !!process.env.CF_PAGES;

export default defineConfig(({ command }) => ({
  plugins: [
    react(),
    ...(command === "build" && !isCloudBuild
      ? [prerender({
          routes: PRERENDER_ROUTES,
          renderer: "@prerenderer/renderer-puppeteer",
          rendererOptions: {
            renderAfterDocumentEvent: "seo-ready",
            timeout: 30000,
            maxConcurrentRoutes: 4,
            headless: true,
            launchOptions: { args: ["--no-sandbox"] },
          },
        })]
      : []),
  ],
}));
```

### A.2 Vue (Vite/Vue 3) SPA → SSG retrofit

Équivalent : `@unhead/vue` pour les meta + `@prerenderer/rollup-plugin` (même setup que A.1, juste remplacer le component React par un composable Vue).

### A.3 Migrer vers Astro (alternative recommandée)

Si la complexité du retrofit dépasse la valeur du retrofit, envisager une migration vers **Astro** :
- SSG par défaut, pas de configuration
- Composants React/Vue/Svelte importables via îlots
- Markdown/MDX natif pour le blog
- Migrations existantes documentées : Next.js → Astro, Gatsby → Astro

### A.4 Piège : config hôte qui annule le SSG

Voir **C21/C22** dans STEP 1.

**Vercel** :
```json
// vercel.json — bon pattern pour SSG
{
  "cleanUrls": true,
  "trailingSlash": false,
  "headers": [...]
  // PAS de catch-all rewrite
}
```

**Netlify** :
```toml
# netlify.toml — bon pattern
[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200
  force = false  # ⚠️ false = filesystem checké d'abord
```

**Cloudflare Pages** :
```
# _redirects — bon pattern (Cloudflare check filesystem AVANT _redirects par défaut)
# Pas besoin de catch-all si tous les fichiers sont prerendus
/*    /index.html   200
```

### A.5 Piège : Helmet (ou équivalent) duplique les meta tags

Si `index.html` a déjà `<meta name="description">` et que `<SEO>` en ajoute une autre, on se retrouve avec 2 descriptions, 2 og:title, etc. Bots prennent la première (souvent la fallback générique).

**Fix React (react-helmet-async)** : marquer les meta de fallback dans `index.html` avec `data-rh="true"` :
```html
<meta name="description" data-rh="true" content="..." />
<link rel="canonical" data-rh="true" href="..." />
<meta property="og:title" data-rh="true" content="..." />
```

**Fix Vue (@unhead/vue)** : `useHead()` gère ça nativement, pas de bidouille nécessaire.

**Fix Next.js Metadata API** : géré nativement, pas de duplication possible.

### A.6 Workflow deploy 4 étapes (PaaS sans Chromium pour Puppeteer)

Si l'hôte build dans le cloud (Vercel, Netlify) et que Chromium n'y est pas dispo :

```bash
# 1. PaaS build : génère le bundle de config (sans prerender, dist/ a juste la SPA shell)
{vercel|netlify} build --prod

# 2. Local build : prérendre les vraies pages dans dist/ (overwrite la SPA shell)
{pm} run build

# 3. Inject les pages prerendues dans le bundle PaaS
rsync -a dist/ .{vercel|netlify}/output/static/   # selon le PaaS

# 4. Deploy le bundle assemblé
{vercel|netlify} deploy --prebuilt --prod
```

À mettre dans un `scripts/deploy.sh` du projet.

**Alternative** : utiliser un PaaS qui supporte Chromium nativement (Cloudflare Workers/Pages avec Browser Rendering API, Render, Railway), OU faire le build dans GitHub Actions avec Chromium installé.

### A.7 Vérification post-deploy

```bash
# Toutes les routes doivent avoir un title unique
for path in "/" "/about" "/blog"; do
  title=$(curl -sL "https://yoursite.com${path}" | grep -oE '<title[^>]*>[^<]+</title>' | head -1)
  echo "$path → $title"
done
# Si tous identiques → SSG cassé (probablement A.4)
```

---

## ANNEXE B : Crawlers AI 2026 — robots.txt

### B.1 Liste à jour des bots AI majeurs

| Bot | Acteur | Fonction | À autoriser ? |
|-----|--------|----------|---------------|
| `GPTBot` | OpenAI | Training (futurs modèles) | Selon politique training |
| `OAI-SearchBot` | OpenAI | ChatGPT Search (citations live) | **Oui** (visibilité) |
| `ChatGPT-User` | OpenAI | Browse à la demande | **Oui** |
| `ClaudeBot` | Anthropic | Training | Selon politique training |
| `Claude-SearchBot` | Anthropic | Citations Claude | **Oui** (visibilité) |
| `anthropic-ai` | Anthropic | Legacy | Idem ClaudeBot |
| `Google-Extended` | Google DeepMind | Training Gemini (séparé de Googlebot !) | Selon politique training |
| `PerplexityBot` | Perplexity | Index | **Oui** (visibilité) |
| `Perplexity-User` | Perplexity | Fetch à la demande | **Oui** |
| `Applebot-Extended` | Apple | Apple Intelligence training | Selon politique training |
| `Meta-ExternalAgent` | Meta | Meta AI | Selon politique training |
| `CCBot` | Common Crawl | Alimente plusieurs LLMs amont | Selon politique |

### B.2 Patterns recommandés

**Stratégie A : autoriser tout** (par défaut, max visibilité, accepter d'être dans le training)
```
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
```

**Stratégie B : visibilité AI sans contribuer au training** (pattern recommandé pour entreprises avec contenu propriétaire)
```
# Bloquer training
User-agent: GPTBot
Disallow: /

User-agent: ClaudeBot
Disallow: /

User-agent: Google-Extended
Disallow: /

User-agent: Applebot-Extended
Disallow: /

User-agent: Meta-ExternalAgent
Disallow: /

# Autoriser citations live (visibilité dans les réponses LLM sans entraîner les modèles)
User-agent: OAI-SearchBot
Allow: /

User-agent: ChatGPT-User
Allow: /

User-agent: Claude-SearchBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: Perplexity-User
Allow: /

# Tous les autres
User-agent: *
Allow: /

Sitemap: https://example.com/sitemap.xml
```

**Stratégie C : tout bloquer** (rare, pour contenu strictement confidentiel — mais Cloudflare ou login sont plus fiables)

### B.3 Test

```bash
for ua in "GPTBot/1.2" "OAI-SearchBot/1.0" "ChatGPT-User/1.0" "ClaudeBot/1.0" "Claude-SearchBot/1.0" "PerplexityBot/1.0" "Perplexity-User/1.0" "Google-Extended" "CCBot/2.0" "Applebot-Extended" "Meta-ExternalAgent"; do
  code=$(curl -sIL -A "$ua" -o /dev/null -w "%{http_code}" {URL})
  echo "$ua → $code"
done
```

---

## ANNEXE C : Schemas Schema.org — éligibilité Rich Results 2026

### C.1 Schemas TOUJOURS éligibles (à utiliser)

| Schema | Rich Result | Notes |
|--------|-------------|-------|
| `Article`, `NewsArticle`, `BlogPosting` | ✓ Article snippet | Image ≥ 1200 px de large |
| `BreadcrumbList` | ✓ Breadcrumbs | À mettre sur toutes les pages profondes |
| `Product` + `Offer` | ✓ Product snippet | Avec `aggregateRating` si avis |
| `Recipe` | ✓ Recipe carousel | Toujours rich-result |
| `Event` | ✓ Event listing | |
| `JobPosting` | ✓ Job listing (Google for Jobs) | |
| `Organization` | ✗ pas de rich result direct, MAIS Knowledge Panel possible | `sameAs` essentiel |
| `LocalBusiness` | ✓ Local pack / Maps | NAP cohérent |
| `WebSite` + `SearchAction` | ✓ Sitelinks Search Box | Si recherche interne dispo |
| `VideoObject` | ✓ Video snippet | Avec `contentUrl` ou `embedUrl` |
| `Person` (sur page auteur) | ✗ pas direct, MAIS boost EEAT | |
| `SoftwareApplication` | ✓ App snippet | Pour apps mobile/desktop |
| `Course` (variante restreinte) | ✓ Course snippet | Restreint depuis 2025 |
| `Review` / `AggregateRating` | ✓ sur Product/LocalBusiness | Pas auto-Review depuis 2023 |

### C.2 Schemas DÉPRÉCIÉS / SUPPRIMÉS

| Schema | Statut | Note |
|--------|--------|------|
| **HowTo** | ❌ Supprimé totalement 2024 | Ne génère plus de rich result, mais **garder dans JSON-LD** : utile pour AI Overviews / Perplexity |
| **FAQPage** | ❌ Supprimé totalement mai 2026 | Idem : garder pour AI (3.2× plus probable d'apparaître en AI Overviews) |
| **Review** auto-publiée sur ses propres produits | ❌ Q3 2023 | Doit venir d'un tiers indépendant |
| Book Actions, Course Info (variante), ClaimReview, Estimated Salary, Learning Video, Special Announcement, Vehicle Listing | ❌ Juin 2025 | 7 schemas retirés en bloc |

### C.3 Validation

- [Schema.org Validator](https://validator.schema.org/) — valide la structure du JSON-LD
- [Google Rich Results Test](https://search.google.com/test/rich-results) — confirme l'éligibilité aux rich results Google
- [Bing Markup Validator](https://www.bing.com/webmaster/diagnostics/markup-validator) — pour Bing/Copilot

---

## RÈGLES IMPORTANTES

1. **Toujours utiliser Lighthouse mobile** (Google indexe mobile-first universel depuis juillet 2024). Desktop seulement en plus si demandé.
2. **Field data > lab data** : CrUX (PSI API) bat Lighthouse synthétique quand dispo. Search Console > rapport Core Web Vitals fait foi.
3. **Adapter au contexte linguistique** : si site en français, utiliser les bons accents et caractères dans toutes les recommandations. Idem pour allemand (umlauts), espagnol (eñe), etc. Détecter via STEP 0.
4. **Pas de recommandations génériques** : chaque finding doit citer la preuve (URL, ligne, valeur exacte) **ET** le correctif exact.
5. **Ne PAS toucher au code de prod sans confirmation explicite**. Mode `fix` édite uniquement le code local et **ne déploie jamais automatiquement**.
6. **Lighthouse peut prendre 30-60s** → afficher un message d'attente. Si `--no-lighthouse`, dire pourquoi (CI offline, déjà fait récemment).
7. **GEO / AI Search est aussi important que le SEO classique en 2026** : ne jamais skip la STEP 6.
8. **Comparer avec un concurrent** (`--compare URL`) : faire le même audit en parallèle et présenter en colonnes côte-à-côte sur les checks clés.
9. **Le rapport est la livraison** : pas de findings sans le rapport markdown sauvegardé.
10. **Si tu recommandes le SSG comme fix** (CRIT-1 typique pour SPA) : LIRE l'**ANNEXE A** d'abord, identifier le stack et l'hôte (STEP 0), et inclure dans la reco les pièges documentés (plugin à utiliser, config hôte, helmet duplicates, workflow deploy). Sinon l'utilisateur va galérer 2-3h.
11. **Sois sceptique du marketing SEO** : beaucoup de blogs d'agences inventent des métriques (« Visual Stability Index ») ou des chiffres (« +40 % visibilité AI »). Privilégier sources Google officielles, web.dev, Bing Webmaster, et experts indépendants reconnus (Lily Ray, Aleyda Solis, Mike King/iPullRank, Brian Dean, Cyrus Shepard, Glenn Gabe).
12. **Ne jamais hardcoder un email, un nom, un chemin** dans une recommandation. Détecter via STEP 0 ou demander via `AskUserQuestion`.

---

## Références (sources de la skill, 2024-2026)

### Officielles
- [web.dev — defining Core Web Vitals thresholds](https://web.dev/articles/defining-core-web-vitals-thresholds)
- [web.dev — Interaction to Next Paint (INP)](https://web.dev/articles/inp)
- [Google Search Central — SEO docs](https://developers.google.com/search/docs)
- [Google Search Central — HowTo/FAQ rich result changes](https://developers.google.com/search/blog/2023/08/howto-faq-changes)
- [Bing Webmaster — AI Performance public preview (fév. 2026)](https://blogs.bing.com/webmaster/February-2026/Introducing-AI-Performance-in-Bing-Webmaster-Tools-Public-Preview)
- [IndexNow protocol](https://www.indexnow.org/)
- [Schema.org](https://schema.org/)

### Experts indépendants
- [Lily Ray (Amsive) — Top experts 2026](https://lilyray.nyc/the-top-10-experts-at-knowing-what-google-really-wants-in-2026/)
- [Mike King (iPullRank) — EEAT/YMYL & AI Search](https://ipullrank.com/eeat-ymyl-ai-search)
- [Aleyda Solis — Hreflang + International SEO](https://www.aleydasolis.com/)
- [Cyrus Shepard — Zyppy](https://zyppy.com/)
- [Glenn Gabe — Core update analyses](https://www.gsqi.com/marketing-blog/)
- [DebugBear — Technical SEO Checklist 2026](https://www.debugbear.com/blog/technical-seo-checklist)
- [Ahrefs — Hreflang complete guide](https://ahrefs.com/blog/hreflang-tags/)

### Outils
- [Lighthouse CLI](https://github.com/GoogleChrome/lighthouse) — `npx lighthouse`
- [PageSpeed Insights API](https://developers.google.com/speed/docs/insights/v5/get-started)
- [Schema.org Validator](https://validator.schema.org/)
- [Google Rich Results Test](https://search.google.com/test/rich-results)
- [Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/) — OG validation
- [HSTS Preload List](https://hstspreload.org/)
- [Bing Webmaster Tools](https://www.bing.com/webmasters/)
