[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fsovM2C4MEjA6CtC-o1LuUojs9VwkjbCzbsrEw1EQS6c":3,"$fu6bJ7-U-028YijHRYoAsEUv7GcxNu9DYPu-6OdAS57g":25},{"_id":4,"slug":5,"__v":6,"author":7,"body":8,"canonical":9,"category":10,"createdAt":11,"date":12,"description":13,"htmlContent":14,"image":15,"imageAlt":15,"readingTime":16,"tags":17,"title":23,"updatedAt":24},"69d9c7d6aa6b273b0c141ecf","signed-exchanges-sxg-accelerer-le-chargement-depuis-google",0,"Equipe Seogard","Google prefetch vos pages dans les SERP — mais uniquement si votre serveur sert des Signed Exchanges (SXG). Sans SXG, le navigateur attend le clic pour initier la requête DNS, puis le TLS handshake, puis le premier byte. Avec SXG, le contenu est déjà chargé en mémoire quand l'utilisateur clique. La différence se mesure en centaines de millisecondes sur le Largest Contentful Paint, parfois plus d'une seconde sur mobile.\n\n## Ce que sont réellement les Signed Exchanges\n\nUn Signed Exchange est un format de réponse HTTP signée cryptographiquement (`application/signed-exchange;v=b3`). Le serveur d'origine signe le contenu avec un certificat spécifique, et n'importe quel intermédiaire — en l'occurrence le cache Google — peut servir cette réponse tout en prouvant qu'elle provient bien de votre domaine. Le navigateur affiche votre URL dans la barre d'adresse, pas une URL Google AMP Cache.\n\nC'est fondamentalement différent d'un cache CDN classique. Un CDN sert du contenu depuis ses PoPs, mais le navigateur sait qu'il parle au CDN. Avec SXG, le navigateur vérifie la signature cryptographique et traite la réponse comme si elle venait directement de votre origine. L'URL, les cookies first-party, le contexte de sécurité : tout reste attaché à votre domaine.\n\n### Le mécanisme de prefetch dans les SERP\n\nQuand Google détecte qu'une page de résultats contient des liens vers des sites servant des SXG valides, il peut injecter un `\u003Clink rel=\"prefetch\">` dans le HTML de la SERP. Ce prefetch télécharge le SXG depuis le cache Google en arrière-plan pendant que l'utilisateur parcourt les résultats. Au moment du clic, le navigateur dispose déjà de la réponse complète — HTML, et potentiellement les sous-ressources critiques.\n\nCe comportement est documenté par Google dans la [spécification Web Packaging](https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html) et implémenté dans Chromium (Chrome, Edge, Opera, Brave). Firefox et Safari ne supportent pas SXG à ce jour, ce qui signifie que le bénéfice de performance ne touche qu'environ 65-70% du trafic navigateur selon les audiences.\n\n### Différence avec AMP et pourquoi SXG l'a remplacé\n\nAMP imposait un framework entier et un sous-ensemble de HTML. Les SXG conservent votre stack technique intacte. Depuis que Google a retiré le traitement préférentiel d'AMP dans le carousel Top Stories (2021), les SXG sont devenus le seul mécanisme permettant le prefetch cross-origin depuis les SERP sans modifier votre code front. Votre page Next.js, Nuxt, ou HTML statique peut être servie en SXG telle quelle.\n\n## Prérequis techniques et obtention du certificat\n\nL'implémentation SXG repose sur trois piliers : un certificat X.509 avec l'extension `CanSignHttpExchanges`, un outil de signature, et une configuration serveur pour le content negotiation.\n\n### Le certificat SXG\n\nUn certificat TLS classique (Let's Encrypt, DigiCert) ne suffit pas. Vous avez besoin d'un certificat avec l'extension OID `1.3.6.1.4.1.11129.2.1.22` (`CanSignHttpExchanges`). À ce jour, DigiCert est le principal fournisseur de certificats SXG pour la production. Google propose également un service via [Web Packager](https://github.com/nicholasgasior/webpkg) pour les cas simples.\n\nLa durée de validité d'un certificat SXG est limitée à 90 jours maximum — contrainte de la spec, pas du fournisseur. Les SXG eux-mêmes expirent au bout de 7 jours maximum. Cela signifie que votre pipeline doit re-signer le contenu régulièrement.\n\nPour générer une clé privée et une CSR (Certificate Signing Request) compatibles :\n\n```bash\n# Générer la clé privée ECDSA P-256 (requise pour SXG)\nopenssl ecparam -genkey -name prime256v1 -out sxg-key.pem\n\n# Générer la CSR avec l'extension CanSignHttpExchanges\nopenssl req -new -key sxg-key.pem -out sxg.csr \\\n  -subj \"/CN=www.example-media.fr\" \\\n  -addext \"1.3.6.1.4.1.11129.2.1.22=DER:05:00\"\n\n# Vérifier que l'extension est présente dans la CSR\nopenssl req -in sxg.csr -text -noout | grep -A1 \"1.3.6.1.4.1.11129\"\n```\n\nRemplacez `www.example-media.fr` par votre domaine réel. L'algorithme ECDSA P-256 est obligatoire — RSA n'est pas accepté pour SXG.\n\n### Web Packager Server\n\nGoogle maintient [Web Packager Server](https://github.com/nicholasgasior/webpkg), un reverse proxy en Go qui intercepte les requêtes SXG, récupère le contenu depuis votre origine, le signe, et le sert. C'est la solution la plus directe pour les déploiements initiaux.\n\nVoici une configuration `webpkgserver.toml` réaliste pour un site média :\n\n```toml\n# /etc/webpkgserver/webpkgserver.toml\n\n[Listen]\n  # Port d'écoute du serveur SXG\n  Host = \"127.0.0.1\"\n  Port = 8080\n  # TLS géré par le reverse proxy amont (Nginx)\n\n[Server]\n  # Domaine pour lequel on signe les exchanges\n  CertURLBase = \"https://www.example-media.fr\"\n  # Durée de validité des SXG (max 7 jours = 604800 sec)\n  ExchangeLifetime = 259200  # 3 jours — marge avant expiration\n\n[SXG]\n  # Chemin vers le certificat SXG (pas le TLS classique)\n  CertPath = \"/etc/webpkgserver/sxg-cert.pem\"\n  KeyPath = \"/etc/webpkgserver/sxg-key.pem\"\n  # OCSP stapling (obligatoire pour SXG)\n  OCSPStaple = true\n\n[Sign]\n  # URLs à signer — regex des chemins éligibles\n  URLPattern = \"https://www.example-media.fr/article/.*\"\n  # Exclure les pages avec contenu personnalisé\n  # (les SXG ne supportent pas le contenu dynamique par utilisateur)\n\n[Fetch]\n  # Timeout pour récupérer le contenu depuis l'origine\n  Timeout = 10\n  # Headers à forward\n  AllowedRequestHeaders = [\"Accept-Language\"]\n```\n\nLe point critique : **les SXG ne peuvent pas contenir de contenu personnalisé**. Pas de `Set-Cookie`, pas de headers `Vary: Cookie`. Si votre page injecte un prénom utilisateur côté serveur, elle n'est pas éligible SXG. Les pages de contenu éditorial, les fiches produits (hors panier), les pages catégorie : c'est le terrain idéal.\n\n## Configuration Nginx pour le content negotiation\n\nLe navigateur demande le SXG via un header `Accept: application/signed-exchange;v=b3`. Votre serveur doit répondre avec le SXG quand ce header est présent, et avec le HTML classique sinon. Ce content negotiation est essentiel — sans lui, les crawlers qui ne supportent pas SXG (dont Googlebot en mode crawl standard) recevront un format qu'ils ne comprennent pas.\n\n```nginx\n# /etc/nginx/conf.d/sxg.conf\n\nupstream webpkgserver {\n    server 127.0.0.1:8080;\n}\n\nupstream origin {\n    server 127.0.0.1:3000;  # Votre app Next.js / Nuxt / autre\n}\n\nserver {\n    listen 443 ssl http2;\n    server_name www.example-media.fr;\n\n    ssl_certificate     /etc/letsencrypt/live/www.example-media.fr/fullchain.pem;\n    ssl_certificate_key /etc/letsencrypt/live/www.example-media.fr/privkey.pem;\n\n    # Content negotiation pour SXG\n    location /article/ {\n        # Vérifier si le client accepte SXG\n        if ($http_accept ~* \"application/signed-exchange;v=b3\") {\n            # Ajouter le header Vary pour les caches intermédiaires\n            add_header Vary \"Accept\" always;\n            # Proxy vers Web Packager Server\n            proxy_pass http://webpkgserver;\n            break;\n        }\n\n        # Contenu HTML classique pour les autres clients\n        add_header Vary \"Accept\" always;\n        proxy_pass http://origin;\n    }\n\n    # Endpoint pour le certificat SXG (Google le récupère)\n    location /.well-known/sxg-certs {\n        alias /etc/webpkgserver/cert-chain/;\n        types {\n            application/cert-chain+cbor cbor;\n        }\n        add_header Access-Control-Allow-Origin \"*\";\n        add_header Cache-Control \"public, max-age=86400\";\n    }\n\n    # Le reste du site — pas de SXG\n    location / {\n        proxy_pass http://origin;\n    }\n}\n```\n\nTrois détails à ne pas rater :\n\n1. **Le header `Vary: Accept`** est indispensable. Sans lui, un CDN intermédiaire (Cloudflare, Fastly) pourrait cacher la version SXG et la servir à un navigateur Firefox qui ne la comprend pas.\n\n2. **L'endpoint `/.well-known/sxg-certs`** sert la chaîne de certificats au format CBOR. Google et le navigateur le récupèrent pour valider la signature. Si cet endpoint renvoie une 404 ou un mauvais Content-Type, tout le mécanisme tombe.\n\n3. **L'OCSP stapling** doit fonctionner. Un SXG avec un certificat dont le statut OCSP n'est pas vérifiable sera rejeté par le navigateur. Vérifiez avec `openssl ocsp` que votre répondeur OCSP est accessible.\n\n## Scénario concret : un média en ligne de 8 000 pages\n\nPrenons le cas d'un site média français publiant 30 articles par jour, avec un catalogue de 8 000 articles actifs indexés. Le trafic provient à 72% de Google Search (mobile dominant à 68%). Le site tourne sur Next.js 15 avec SSR, déployé sur des VPS OVH derrière Cloudflare.\n\n### Situation avant SXG\n\nLes Core Web Vitals mesurés en champ (CrUX) montrent un LCP P75 de 2.8s sur mobile depuis les SERP. Le Time to First Byte est correct (450ms au P75), mais le gap entre le clic SERP et le premier paint reste élevé à cause de la chaîne : DNS → TLS → TTFB → parsing HTML → chargement CSS critique.\n\nDans la Search Console, les données Page Experience montrent que 61% des pages passent le seuil \"Good\" pour le LCP (\u003C 2.5s). Les 39% restantes sont pénalisées par la latence réseau, surtout sur les connexions 4G lentes.\n\n### Déploiement SXG\n\nL'équipe déploie Web Packager Server devant le rendu Next.js, avec la config Nginx ci-dessus. Seules les pages `/article/*` sont éligibles — les pages catégorie avec filtres dynamiques et la page d'accueil personnalisée sont exclues.\n\nLe pipeline :\n1. Next.js rend le HTML en SSR (comme avant)\n2. Web Packager intercepte les requêtes avec `Accept: application/signed-exchange;v=b3`\n3. Il récupère le HTML depuis l'origine locale (127.0.0.1:3000), le signe, et le renvoie\n4. Cloudflare cache le SXG avec `Vary: Accept` (cache key distincte du HTML)\n5. Google crawle les SXG, les valide, et les stocke dans son cache de prefetch\n\n### Validation et debugging\n\nAvant de déployer en production, validez vos SXG avec l'outil en ligne de commande `dump-signedexchange` :\n\n```bash\n# Installer l'outil Go\ngo install github.com/nicholasgasior/webpkg/cmd/dump-signedexchange@latest\n\n# Télécharger un SXG depuis votre serveur\ncurl -s -H \"Accept: application/signed-exchange;v=b3\" \\\n  \"https://www.example-media.fr/article/politique-europeenne-2026\" \\\n  -o test.sxg\n\n# Valider le SXG\ndump-signedexchange -i test.sxg -verify\n\n# Sortie attendue :\n# The exchange is valid.\n# URI: https://www.example-media.fr/article/politique-europeenne-2026\n# Status: 200\n# Signature expires: 2026-04-14T...\n```\n\nDans Chrome DevTools, l'onglet Network affiche le SXG comme une requête avec un badge \"signed-exchange\" dans la colonne Type. Vous pouvez aussi vérifier dans `chrome://web-package-internals/` pour le debugging avancé.\n\nLa Search Console expose les erreurs SXG dans le rapport \"Signed Exchanges\" (sous Indexation > Pages). Les erreurs les plus fréquentes : certificat expiré, OCSP non valide, header `Vary` manquant, taille du SXG dépassant 8 Mo.\n\n### Résultats mesurés\n\nAprès 6 semaines de déploiement progressif (d'abord 500 articles, puis l'ensemble du catalogue) :\n\n- **LCP P75 mobile depuis les SERP** : 2.8s → 1.7s (réduction de ~39%)\n- **Pages \"Good\" LCP** : 61% → 84%\n- **FID / INP** : inchangé (SXG n'affecte que le chargement initial, pas l'interactivité post-load)\n- **CTR organique moyen** : +3.2% sur les positions 2-5 (hypothèse : le prefetch visible dans Chrome donne un signal de rapidité qui influence le comportement de clic)\n\nLe gain de LCP est mécaniquement attendu : on élimine le temps réseau entre le clic et la réception du HTML. Sur une connexion 4G typique (100ms RTT), c'est DNS (50-100ms) + TLS (100-200ms) + TTFB (200-500ms) qui disparaissent. Le HTML est déjà en mémoire.\n\n## Edge cases et limitations à connaître\n\n### Contenu personnalisé : l'incompatibilité fondamentale\n\nLes SXG sont des réponses statiques signées. Tout ce qui varie par utilisateur — panier e-commerce dans le header, message \"Bonjour Jean-Pierre\", suggestions personnalisées — ne peut pas être dans le SXG. La solution classique : servir le shell statique en SXG, puis hydrater le contenu personnalisé côté client via des appels API.\n\nPour un site e-commerce, cela signifie que la fiche produit (titre, description, prix, images) est dans le SXG, mais le nombre d'articles dans le panier (affiché dans le header) est chargé en JavaScript après le paint initial. Si votre architecture injecte déjà ces éléments côté client (ce qui est le cas dans la majorité des headless commerce), vous n'avez rien à changer. Si votre SSR injecte des données utilisateur dans le HTML, il faut refactorer — et c'est un chantier non trivial.\n\n### Headers interdits\n\nLa spec SXG interdit certains headers dans la réponse signée : `Set-Cookie`, `Authentication-Info`, `Optional-WWW-Authenticate`, `Proxy-Authenticate`, `Proxy-Authorization`, `Sec-WebSocket-Accept`, `WWW-Authenticate`. Si votre serveur les inclut dans la réponse, Web Packager les strippera, mais vérifiez que cela ne casse pas votre logique applicative.\n\n### Impact sur le crawl budget\n\nGoogle crawle les versions SXG en plus des versions HTML classiques. Pour un site de 8 000 pages, cela signifie potentiellement un doublement des requêtes de crawl — chaque URL est crawlée en version HTML (pour l'indexation) et en version SXG (pour le prefetch cache). En pratique, Google ne re-crawle pas les SXG à la même fréquence que le HTML, mais sur un site avec des dizaines de milliers de pages, surveillez le rapport de crawl dans la Search Console. Si vous constatez une pression anormale sur votre infrastructure, limitez les URLs éligibles SXG via le `URLPattern` de Web Packager.\n\nLe lien avec la gestion du [rendering budget](/blog/rendering-budget-de-google-combien-de-javascript-est-trop) est direct : si votre site est déjà sous pression côté crawl, ajouter SXG sans supervision risque de dégrader la fraîcheur d'indexation de vos pages les moins prioritaires.\n\n### Interaction avec le CDN et le cache edge\n\nSi vous utilisez un CDN qui fait de la [modification des réponses HTTP au niveau edge](/blog/edge-seo-modifier-les-reponses-http-au-niveau-cdn), attention aux interactions. Un worker Cloudflare qui modifie le HTML (injection de scripts analytics, modification de headers) après la signature SXG invalidera la signature. Le SXG doit être signé **après** toute transformation edge. Deux options :\n\n1. Signer au niveau edge (Cloudflare Workers avec la lib `sxg-rs` de Google)\n2. Signer à l'origine et interdire toute modification edge sur les réponses SXG\n\nCloudflare propose d'ailleurs un support SXG natif via leur feature \"Automatic Signed Exchanges\" (disponible sur les plans Business et Enterprise). C'est la voie de moindre résistance si vous êtes déjà client Cloudflare — pas besoin de Web Packager, pas besoin de gérer le certificat SXG vous-même.\n\n### Navigateurs non supportés\n\nFirefox et Safari ne supportent pas SXG. Vos utilisateurs iOS (Safari) et les utilisateurs Firefox ne bénéficient d'aucun gain. Le content negotiation via `Vary: Accept` garantit qu'ils reçoivent le HTML classique, mais il n'y a aucun mécanisme de fallback pour le prefetch : ces navigateurs ne seront jamais prefetch depuis les SERP Google.\n\nPour ces utilisateurs, les optimisations classiques de performance restent votre seul levier : compression Brotli, préconnexion DNS, réduction du critical path CSS, lazy loading agressif.\n\n## Monitoring en production et détection des régressions\n\nUn SXG qui cesse de fonctionner silencieusement est pire qu'un SXG jamais déployé. Vous perdez le bénéfice de performance sans alerte, et les métriques CrUX se dégradent progressivement sur 28 jours avant que vous ne le remarquiez dans la Search Console.\n\n### Points de surveillance critiques\n\n**Expiration du certificat SXG** : 90 jours max. Si votre pipeline de renouvellement automatique (similaire à certbot pour Let's Encrypt) échoue, tous vos SXG deviennent invalides. Mettez une alerte à J-14.\n\n**Expiration des SXG individuels** : 7 jours max. Si Web Packager est down pendant 7 jours, tous les SXG en cache expirent et Google cesse le prefetch. Monitorez le process Web Packager et le temps de réponse sur le port 8080.\n\n**Validité OCSP** : l'agrafage OCSP doit être fonctionnel. Un répondeur OCSP down chez votre CA (DigiCert) rend vos SXG invalides le temps de la panne. Vous n'y pouvez rien, mais vous devez le détecter.\n\n**Header `Content-Type`** : la réponse SXG doit avoir exactement `application/signed-exchange;v=b3`. Un reverse proxy mal configuré qui réécrit en `text/html` casse tout.\n\nUn outil de monitoring comme Seogard détecte ces régressions automatiquement en surveillant les headers HTTP et les réponses pour chaque URL critique, évitant le scénario où un certificat expire un vendredi soir sans que personne ne s'en aperçoive.\n\n### Automatiser la vérification\n\nIntégrez un check dans votre CI/CD ou votre cron de monitoring :\n\n```bash\n#!/bin/bash\n# check-sxg-health.sh — À exécuter toutes les heures\n\nDOMAIN=\"www.example-media.fr\"\nTEST_URL=\"https://${DOMAIN}/article/politique-europeenne-2026\"\n\n# 1. Vérifier que le serveur répond en SXG\nSTATUS=$(curl -s -o /dev/null -w \"%{http_code}\" \\\n  -H \"Accept: application/signed-exchange;v=b3\" \\\n  \"${TEST_URL}\")\n\nif [ \"$STATUS\" != \"200\" ]; then\n  echo \"CRITICAL: SXG endpoint returned HTTP ${STATUS}\" | \\\n    mail -s \"SXG Health Check Failed\" ops@example-media.fr\n  exit 1\nfi\n\n# 2. Vérifier le Content-Type\nCONTENT_TYPE=$(curl -s -I \\\n  -H \"Accept: application/signed-exchange;v=b3\" \\\n  \"${TEST_URL}\" | grep -i \"content-type:\" | tr -d '\\r')\n\nif [[ ! \"$CONTENT_TYPE\" =~ \"signed-exchange\" ]]; then\n  echo \"CRITICAL: Wrong Content-Type: ${CONTENT_TYPE}\"\n  exit 1\nfi\n\n# 3. Vérifier l'expiration du certificat SXG\nCERT_EXPIRY=$(openssl x509 -in /etc/webpkgserver/sxg-cert.pem \\\n  -noout -enddate | cut -d= -f2)\nEXPIRY_EPOCH=$(date -d \"${CERT_EXPIRY}\" +%s)\nNOW_EPOCH=$(date +%s)\nDAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))\n\nif [ \"$DAYS_LEFT\" -lt 14 ]; then\n  echo \"WARNING: SXG cert expires in ${DAYS_LEFT} days\"\n  exit 1\nfi\n\n# 4. Vérifier l'endpoint cert-chain\nCERT_STATUS=$(curl -s -o /dev/null -w \"%{http_code}\" \\\n  \"https://${DOMAIN}/.well-known/sxg-certs\")\n\nif [ \"$CERT_STATUS\" != \"200\" ]; then\n  echo \"CRITICAL: cert-chain endpoint returned HTTP ${CERT_STATUS}\"\n  exit 1\nfi\n\necho \"OK: SXG healthy, cert expires in ${DAYS_LEFT} days\"\n```\n\n### Suivre l'impact dans vos KPIs\n\nL'impact des SXG se mesure principalement via :\n\n- **CrUX LCP** segmenté par navigateur (Chrome vs autres) dans le [rapport Page Experience de la Search Console](/blog/mesurer-l-impact-seo-technique-quels-kpis-suivre)\n- **CTR par position** dans la Search Console — segmentez par device pour isoler le mobile Chrome\n- **Nombre de SXG valides** dans le rapport Search Console dédié\n- Les [données de la Search Console API](/blog/search-console-api-automatiser-le-reporting-seo) pour automatiser le suivi des métriques de performance par groupe de pages SXG vs non-SXG\n\nAttention au biais de comparaison : ne comparez pas le LCP des pages SXG vs non-SXG directement, car vous avez probablement activé SXG sur vos pages les plus performantes d'abord. Utilisez un A/B test par échantillonnage d'URLs similaires, en respectant les bonnes pratiques d'[A/B testing SEO](/blog/a-b-testing-seo-tester-sans-penaliser-le-referencement).\n\n## Quand NE PAS implémenter les SXG\n\nLes SXG ne sont pas une silver bullet. Voici les cas où le ratio effort/bénéfice est défavorable :\n\n**Sites à faible trafic Google mobile Chrome** : si votre audience est B2B desktop avec 40% de Firefox/Safari, le pool d'utilisateurs éligibles au prefetch SXG est trop petit pour justifier la complexité opérationnelle.\n\n**Sites entièrement personnalisés côté serveur** : un SaaS dont chaque page SSR injecte des données utilisateur n'a aucune page éligible SXG sans refactoring majeur.\n\n**Stacks sans SSR** : si votre site est une SPA React client-side rendue, le HTML servi est un shell vide avec un `\u003Cdiv id=\"root\">\u003C/div>`. Signer ce shell en SXG est inutile — le LCP dépend du JavaScript exécuté après, pas du temps de réception du HTML. Réglez d'abord votre problème de [rendering budget](/blog/rendering-budget-de-google-combien-de-javascript-est-trop) avant de penser aux SXG.\n\n**Infrastructure edge incompatible** : si vous êtes sur un plan Cloudflare Free/Pro sans Workers, ou sur un hébergement mutualisé sans accès à la config Nginx, le déploiement de Web Packager est simplement impossible.\n\nLe meilleur candidat SXG : un site de contenu (média, e-commerce, documentation) avec du SSR propre, un trafic organique Google significatif, et une audience majoritairement Chrome mobile.\n\n## Wrap-up\n\nLes Signed Exchanges éliminent la latence réseau entre le clic SERP et le premier byte — un gain mécanique de 300 à 800ms sur le LCP mobile que aucune optimisation front-end ne peut reproduire. Le coût d'entrée est réel (certificat SXG, Web Packager, content negotiation, monitoring), mais pour un site de contenu avec du trafic Google Chrome significatif, le ROI est mesurable en semaines. Mettez en place un monitoring continu des headers SXG et de l'expiration des certificats — un outil comme Seogard automatise cette surveillance — parce qu'un SXG cassé silencieusement est une régression de performance invisible jusqu'au prochain rapport CrUX.\n```","https://seogard.io/blog/signed-exchanges-sxg-accelerer-le-chargement-depuis-google","Avancé","2026-04-11T04:02:30.018Z","2026-04-11","Implémentez les Signed Exchanges pour que Google prefetch vos pages directement depuis les SERP. Guide technique complet avec config, certificats et monitoring.","\u003Cp>Google prefetch vos pages dans les SERP — mais uniquement si votre serveur sert des Signed Exchanges (SXG). Sans SXG, le navigateur attend le clic pour initier la requête DNS, puis le TLS handshake, puis le premier byte. Avec SXG, le contenu est déjà chargé en mémoire quand l'utilisateur clique. La différence se mesure en centaines de millisecondes sur le Largest Contentful Paint, parfois plus d'une seconde sur mobile.\u003C/p>\n\u003Ch2>Ce que sont réellement les Signed Exchanges\u003C/h2>\n\u003Cp>Un Signed Exchange est un format de réponse HTTP signée cryptographiquement (\u003Ccode>application/signed-exchange;v=b3\u003C/code>). Le serveur d'origine signe le contenu avec un certificat spécifique, et n'importe quel intermédiaire — en l'occurrence le cache Google — peut servir cette réponse tout en prouvant qu'elle provient bien de votre domaine. Le navigateur affiche votre URL dans la barre d'adresse, pas une URL Google AMP Cache.\u003C/p>\n\u003Cp>C'est fondamentalement différent d'un cache CDN classique. Un CDN sert du contenu depuis ses PoPs, mais le navigateur sait qu'il parle au CDN. Avec SXG, le navigateur vérifie la signature cryptographique et traite la réponse comme si elle venait directement de votre origine. L'URL, les cookies first-party, le contexte de sécurité : tout reste attaché à votre domaine.\u003C/p>\n\u003Ch3>Le mécanisme de prefetch dans les SERP\u003C/h3>\n\u003Cp>Quand Google détecte qu'une page de résultats contient des liens vers des sites servant des SXG valides, il peut injecter un \u003Ccode>&#x3C;link rel=\"prefetch\">\u003C/code> dans le HTML de la SERP. Ce prefetch télécharge le SXG depuis le cache Google en arrière-plan pendant que l'utilisateur parcourt les résultats. Au moment du clic, le navigateur dispose déjà de la réponse complète — HTML, et potentiellement les sous-ressources critiques.\u003C/p>\n\u003Cp>Ce comportement est documenté par Google dans la \u003Ca href=\"https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html\">spécification Web Packaging\u003C/a> et implémenté dans Chromium (Chrome, Edge, Opera, Brave). Firefox et Safari ne supportent pas SXG à ce jour, ce qui signifie que le bénéfice de performance ne touche qu'environ 65-70% du trafic navigateur selon les audiences.\u003C/p>\n\u003Ch3>Différence avec AMP et pourquoi SXG l'a remplacé\u003C/h3>\n\u003Cp>AMP imposait un framework entier et un sous-ensemble de HTML. Les SXG conservent votre stack technique intacte. Depuis que Google a retiré le traitement préférentiel d'AMP dans le carousel Top Stories (2021), les SXG sont devenus le seul mécanisme permettant le prefetch cross-origin depuis les SERP sans modifier votre code front. Votre page Next.js, Nuxt, ou HTML statique peut être servie en SXG telle quelle.\u003C/p>\n\u003Ch2>Prérequis techniques et obtention du certificat\u003C/h2>\n\u003Cp>L'implémentation SXG repose sur trois piliers : un certificat X.509 avec l'extension \u003Ccode>CanSignHttpExchanges\u003C/code>, un outil de signature, et une configuration serveur pour le content negotiation.\u003C/p>\n\u003Ch3>Le certificat SXG\u003C/h3>\n\u003Cp>Un certificat TLS classique (Let's Encrypt, DigiCert) ne suffit pas. Vous avez besoin d'un certificat avec l'extension OID \u003Ccode>1.3.6.1.4.1.11129.2.1.22\u003C/code> (\u003Ccode>CanSignHttpExchanges\u003C/code>). À ce jour, DigiCert est le principal fournisseur de certificats SXG pour la production. Google propose également un service via \u003Ca href=\"https://github.com/nicholasgasior/webpkg\">Web Packager\u003C/a> pour les cas simples.\u003C/p>\n\u003Cp>La durée de validité d'un certificat SXG est limitée à 90 jours maximum — contrainte de la spec, pas du fournisseur. Les SXG eux-mêmes expirent au bout de 7 jours maximum. Cela signifie que votre pipeline doit re-signer le contenu régulièrement.\u003C/p>\n\u003Cp>Pour générer une clé privée et une CSR (Certificate Signing Request) compatibles :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Générer la clé privée ECDSA P-256 (requise pour SXG)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">openssl\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> ecparam\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -genkey\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -name\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> prime256v1\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -out\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> sxg-key.pem\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Générer la CSR avec l'extension CanSignHttpExchanges\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">openssl\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> req\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -new\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -key\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> sxg-key.pem\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -out\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> sxg.csr\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  -subj\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"/CN=www.example-media.fr\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  -addext\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"1.3.6.1.4.1.11129.2.1.22=DER:05:00\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Vérifier que l'extension est présente dans la CSR\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">openssl\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> req\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -in\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> sxg.csr\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -text\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -noout\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> grep\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -A1\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"1.3.6.1.4.1.11129\"\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Remplacez \u003Ccode>www.example-media.fr\u003C/code> par votre domaine réel. L'algorithme ECDSA P-256 est obligatoire — RSA n'est pas accepté pour SXG.\u003C/p>\n\u003Ch3>Web Packager Server\u003C/h3>\n\u003Cp>Google maintient \u003Ca href=\"https://github.com/nicholasgasior/webpkg\">Web Packager Server\u003C/a>, un reverse proxy en Go qui intercepte les requêtes SXG, récupère le contenu depuis votre origine, le signe, et le sert. C'est la solution la plus directe pour les déploiements initiaux.\u003C/p>\n\u003Cp>Voici une configuration \u003Ccode>webpkgserver.toml\u003C/code> réaliste pour un site média :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># /etc/webpkgserver/webpkgserver.toml\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">[\u003C/span>\u003Cspan style=\"color:#B392F0\">Listen\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # Port d'écoute du serveur SXG\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  Host = \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"127.0.0.1\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  Port = \u003C/span>\u003Cspan style=\"color:#79B8FF\">8080\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # TLS géré par le reverse proxy amont (Nginx)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">[\u003C/span>\u003Cspan style=\"color:#B392F0\">Server\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # Domaine pour lequel on signe les exchanges\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  CertURLBase = \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"https://www.example-media.fr\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # Durée de validité des SXG (max 7 jours = 604800 sec)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  ExchangeLifetime = \u003C/span>\u003Cspan style=\"color:#79B8FF\">259200\u003C/span>\u003Cspan style=\"color:#6A737D\">  # 3 jours — marge avant expiration\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">[\u003C/span>\u003Cspan style=\"color:#B392F0\">SXG\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # Chemin vers le certificat SXG (pas le TLS classique)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  CertPath = \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"/etc/webpkgserver/sxg-cert.pem\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  KeyPath = \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"/etc/webpkgserver/sxg-key.pem\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # OCSP stapling (obligatoire pour SXG)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  OCSPStaple = \u003C/span>\u003Cspan style=\"color:#79B8FF\">true\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">[\u003C/span>\u003Cspan style=\"color:#B392F0\">Sign\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # URLs à signer — regex des chemins éligibles\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  URLPattern = \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"https://www.example-media.fr/article/.*\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # Exclure les pages avec contenu personnalisé\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # (les SXG ne supportent pas le contenu dynamique par utilisateur)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">[\u003C/span>\u003Cspan style=\"color:#B392F0\">Fetch\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # Timeout pour récupérer le contenu depuis l'origine\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  Timeout = \u003C/span>\u003Cspan style=\"color:#79B8FF\">10\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">  # Headers à forward\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  AllowedRequestHeaders = [\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Accept-Language\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Le point critique : \u003Cstrong>les SXG ne peuvent pas contenir de contenu personnalisé\u003C/strong>. Pas de \u003Ccode>Set-Cookie\u003C/code>, pas de headers \u003Ccode>Vary: Cookie\u003C/code>. Si votre page injecte un prénom utilisateur côté serveur, elle n'est pas éligible SXG. Les pages de contenu éditorial, les fiches produits (hors panier), les pages catégorie : c'est le terrain idéal.\u003C/p>\n\u003Ch2>Configuration Nginx pour le content negotiation\u003C/h2>\n\u003Cp>Le navigateur demande le SXG via un header \u003Ccode>Accept: application/signed-exchange;v=b3\u003C/code>. Votre serveur doit répondre avec le SXG quand ce header est présent, et avec le HTML classique sinon. Ce content negotiation est essentiel — sans lui, les crawlers qui ne supportent pas SXG (dont Googlebot en mode crawl standard) recevront un format qu'ils ne comprennent pas.\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># /etc/nginx/conf.d/sxg.conf\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">upstream\u003C/span>\u003Cspan style=\"color:#B392F0\"> webpkgserver \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    server\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> 127.0.0.1:8080;\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\">upstream\u003C/span>\u003Cspan style=\"color:#B392F0\"> origin \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    server\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> 127.0.0.1:3000;  \u003C/span>\u003Cspan style=\"color:#6A737D\"># Votre app Next.js / Nuxt / autre\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\">server\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    listen \u003C/span>\u003Cspan style=\"color:#79B8FF\">443\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ssl http2;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    server_name \u003C/span>\u003Cspan style=\"color:#E1E4E8\">www.example-media.fr;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    ssl_certificate \u003C/span>\u003Cspan style=\"color:#E1E4E8\">    /etc/letsencrypt/live/www.example-media.fr/fullchain.pem;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    ssl_certificate_key \u003C/span>\u003Cspan style=\"color:#E1E4E8\">/etc/letsencrypt/live/www.example-media.fr/privkey.pem;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Content negotiation pour SXG\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    location\u003C/span>\u003Cspan style=\"color:#B392F0\"> /article/ \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">        # Vérifier si le client accepte SXG\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ($http_accept \u003C/span>\u003Cspan style=\"color:#F97583\">~* \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"application/signed-exchange;v=b3\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">            # Ajouter le header Vary pour les caches intermédiaires\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">            add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Vary \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Accept\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> always;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">            # Proxy vers Web Packager Server\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">            proxy_pass \u003C/span>\u003Cspan style=\"color:#E1E4E8\">http://webpkgserver;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">            break\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\">        # Contenu HTML classique pour les autres clients\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Vary \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Accept\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> always;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        proxy_pass \u003C/span>\u003Cspan style=\"color:#E1E4E8\">http://origin;\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\">    # Endpoint pour le certificat SXG (Google le récupère)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    location\u003C/span>\u003Cspan style=\"color:#B392F0\"> /.well-known/sxg-certs \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        alias \u003C/span>\u003Cspan style=\"color:#E1E4E8\">/etc/webpkgserver/cert-chain/;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        types\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">            application/cert-chain+cbor\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> cbor;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Access-Control-Allow-Origin \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"*\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Cache-Control \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"public, max-age=86400\"\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\">    # Le reste du site — pas de SXG\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    location\u003C/span>\u003Cspan style=\"color:#B392F0\"> / \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        proxy_pass \u003C/span>\u003Cspan style=\"color:#E1E4E8\">http://origin;\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>Trois détails à ne pas rater :\u003C/p>\n\u003Col>\n\u003Cli>\n\u003Cp>\u003Cstrong>Le header \u003Ccode>Vary: Accept\u003C/code>\u003C/strong> est indispensable. Sans lui, un CDN intermédiaire (Cloudflare, Fastly) pourrait cacher la version SXG et la servir à un navigateur Firefox qui ne la comprend pas.\u003C/p>\n\u003C/li>\n\u003Cli>\n\u003Cp>\u003Cstrong>L'endpoint \u003Ccode>/.well-known/sxg-certs\u003C/code>\u003C/strong> sert la chaîne de certificats au format CBOR. Google et le navigateur le récupèrent pour valider la signature. Si cet endpoint renvoie une 404 ou un mauvais Content-Type, tout le mécanisme tombe.\u003C/p>\n\u003C/li>\n\u003Cli>\n\u003Cp>\u003Cstrong>L'OCSP stapling\u003C/strong> doit fonctionner. Un SXG avec un certificat dont le statut OCSP n'est pas vérifiable sera rejeté par le navigateur. Vérifiez avec \u003Ccode>openssl ocsp\u003C/code> que votre répondeur OCSP est accessible.\u003C/p>\n\u003C/li>\n\u003C/ol>\n\u003Ch2>Scénario concret : un média en ligne de 8 000 pages\u003C/h2>\n\u003Cp>Prenons le cas d'un site média français publiant 30 articles par jour, avec un catalogue de 8 000 articles actifs indexés. Le trafic provient à 72% de Google Search (mobile dominant à 68%). Le site tourne sur Next.js 15 avec SSR, déployé sur des VPS OVH derrière Cloudflare.\u003C/p>\n\u003Ch3>Situation avant SXG\u003C/h3>\n\u003Cp>Les Core Web Vitals mesurés en champ (CrUX) montrent un LCP P75 de 2.8s sur mobile depuis les SERP. Le Time to First Byte est correct (450ms au P75), mais le gap entre le clic SERP et le premier paint reste élevé à cause de la chaîne : DNS → TLS → TTFB → parsing HTML → chargement CSS critique.\u003C/p>\n\u003Cp>Dans la Search Console, les données Page Experience montrent que 61% des pages passent le seuil \"Good\" pour le LCP (&#x3C; 2.5s). Les 39% restantes sont pénalisées par la latence réseau, surtout sur les connexions 4G lentes.\u003C/p>\n\u003Ch3>Déploiement SXG\u003C/h3>\n\u003Cp>L'équipe déploie Web Packager Server devant le rendu Next.js, avec la config Nginx ci-dessus. Seules les pages \u003Ccode>/article/*\u003C/code> sont éligibles — les pages catégorie avec filtres dynamiques et la page d'accueil personnalisée sont exclues.\u003C/p>\n\u003Cp>Le pipeline :\u003C/p>\n\u003Col>\n\u003Cli>Next.js rend le HTML en SSR (comme avant)\u003C/li>\n\u003Cli>Web Packager intercepte les requêtes avec \u003Ccode>Accept: application/signed-exchange;v=b3\u003C/code>\u003C/li>\n\u003Cli>Il récupère le HTML depuis l'origine locale (127.0.0.1:3000), le signe, et le renvoie\u003C/li>\n\u003Cli>Cloudflare cache le SXG avec \u003Ccode>Vary: Accept\u003C/code> (cache key distincte du HTML)\u003C/li>\n\u003Cli>Google crawle les SXG, les valide, et les stocke dans son cache de prefetch\u003C/li>\n\u003C/ol>\n\u003Ch3>Validation et debugging\u003C/h3>\n\u003Cp>Avant de déployer en production, validez vos SXG avec l'outil en ligne de commande \u003Ccode>dump-signedexchange\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:#6A737D\"># Installer l'outil Go\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">go\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> install\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> github.com/nicholasgasior/webpkg/cmd/dump-signedexchange@latest\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Télécharger un SXG depuis votre serveur\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">curl\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -s\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -H\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Accept: application/signed-exchange;v=b3\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  \"https://www.example-media.fr/article/politique-europeenne-2026\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  -o\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> test.sxg\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Valider le SXG\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">dump-signedexchange\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -i\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> test.sxg\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -verify\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Sortie attendue :\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># The exchange is valid.\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># URI: https://www.example-media.fr/article/politique-europeenne-2026\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Status: 200\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Signature expires: 2026-04-14T...\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Dans Chrome DevTools, l'onglet Network affiche le SXG comme une requête avec un badge \"signed-exchange\" dans la colonne Type. Vous pouvez aussi vérifier dans \u003Ccode>chrome://web-package-internals/\u003C/code> pour le debugging avancé.\u003C/p>\n\u003Cp>La Search Console expose les erreurs SXG dans le rapport \"Signed Exchanges\" (sous Indexation > Pages). Les erreurs les plus fréquentes : certificat expiré, OCSP non valide, header \u003Ccode>Vary\u003C/code> manquant, taille du SXG dépassant 8 Mo.\u003C/p>\n\u003Ch3>Résultats mesurés\u003C/h3>\n\u003Cp>Après 6 semaines de déploiement progressif (d'abord 500 articles, puis l'ensemble du catalogue) :\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>LCP P75 mobile depuis les SERP\u003C/strong> : 2.8s → 1.7s (réduction de ~39%)\u003C/li>\n\u003Cli>\u003Cstrong>Pages \"Good\" LCP\u003C/strong> : 61% → 84%\u003C/li>\n\u003Cli>\u003Cstrong>FID / INP\u003C/strong> : inchangé (SXG n'affecte que le chargement initial, pas l'interactivité post-load)\u003C/li>\n\u003Cli>\u003Cstrong>CTR organique moyen\u003C/strong> : +3.2% sur les positions 2-5 (hypothèse : le prefetch visible dans Chrome donne un signal de rapidité qui influence le comportement de clic)\u003C/li>\n\u003C/ul>\n\u003Cp>Le gain de LCP est mécaniquement attendu : on élimine le temps réseau entre le clic et la réception du HTML. Sur une connexion 4G typique (100ms RTT), c'est DNS (50-100ms) + TLS (100-200ms) + TTFB (200-500ms) qui disparaissent. Le HTML est déjà en mémoire.\u003C/p>\n\u003Ch2>Edge cases et limitations à connaître\u003C/h2>\n\u003Ch3>Contenu personnalisé : l'incompatibilité fondamentale\u003C/h3>\n\u003Cp>Les SXG sont des réponses statiques signées. Tout ce qui varie par utilisateur — panier e-commerce dans le header, message \"Bonjour Jean-Pierre\", suggestions personnalisées — ne peut pas être dans le SXG. La solution classique : servir le shell statique en SXG, puis hydrater le contenu personnalisé côté client via des appels API.\u003C/p>\n\u003Cp>Pour un site e-commerce, cela signifie que la fiche produit (titre, description, prix, images) est dans le SXG, mais le nombre d'articles dans le panier (affiché dans le header) est chargé en JavaScript après le paint initial. Si votre architecture injecte déjà ces éléments côté client (ce qui est le cas dans la majorité des headless commerce), vous n'avez rien à changer. Si votre SSR injecte des données utilisateur dans le HTML, il faut refactorer — et c'est un chantier non trivial.\u003C/p>\n\u003Ch3>Headers interdits\u003C/h3>\n\u003Cp>La spec SXG interdit certains headers dans la réponse signée : \u003Ccode>Set-Cookie\u003C/code>, \u003Ccode>Authentication-Info\u003C/code>, \u003Ccode>Optional-WWW-Authenticate\u003C/code>, \u003Ccode>Proxy-Authenticate\u003C/code>, \u003Ccode>Proxy-Authorization\u003C/code>, \u003Ccode>Sec-WebSocket-Accept\u003C/code>, \u003Ccode>WWW-Authenticate\u003C/code>. Si votre serveur les inclut dans la réponse, Web Packager les strippera, mais vérifiez que cela ne casse pas votre logique applicative.\u003C/p>\n\u003Ch3>Impact sur le crawl budget\u003C/h3>\n\u003Cp>Google crawle les versions SXG en plus des versions HTML classiques. Pour un site de 8 000 pages, cela signifie potentiellement un doublement des requêtes de crawl — chaque URL est crawlée en version HTML (pour l'indexation) et en version SXG (pour le prefetch cache). En pratique, Google ne re-crawle pas les SXG à la même fréquence que le HTML, mais sur un site avec des dizaines de milliers de pages, surveillez le rapport de crawl dans la Search Console. Si vous constatez une pression anormale sur votre infrastructure, limitez les URLs éligibles SXG via le \u003Ccode>URLPattern\u003C/code> de Web Packager.\u003C/p>\n\u003Cp>Le lien avec la gestion du \u003Ca href=\"/blog/rendering-budget-de-google-combien-de-javascript-est-trop\">rendering budget\u003C/a> est direct : si votre site est déjà sous pression côté crawl, ajouter SXG sans supervision risque de dégrader la fraîcheur d'indexation de vos pages les moins prioritaires.\u003C/p>\n\u003Ch3>Interaction avec le CDN et le cache edge\u003C/h3>\n\u003Cp>Si vous utilisez un CDN qui fait de la \u003Ca href=\"/blog/edge-seo-modifier-les-reponses-http-au-niveau-cdn\">modification des réponses HTTP au niveau edge\u003C/a>, attention aux interactions. Un worker Cloudflare qui modifie le HTML (injection de scripts analytics, modification de headers) après la signature SXG invalidera la signature. Le SXG doit être signé \u003Cstrong>après\u003C/strong> toute transformation edge. Deux options :\u003C/p>\n\u003Col>\n\u003Cli>Signer au niveau edge (Cloudflare Workers avec la lib \u003Ccode>sxg-rs\u003C/code> de Google)\u003C/li>\n\u003Cli>Signer à l'origine et interdire toute modification edge sur les réponses SXG\u003C/li>\n\u003C/ol>\n\u003Cp>Cloudflare propose d'ailleurs un support SXG natif via leur feature \"Automatic Signed Exchanges\" (disponible sur les plans Business et Enterprise). C'est la voie de moindre résistance si vous êtes déjà client Cloudflare — pas besoin de Web Packager, pas besoin de gérer le certificat SXG vous-même.\u003C/p>\n\u003Ch3>Navigateurs non supportés\u003C/h3>\n\u003Cp>Firefox et Safari ne supportent pas SXG. Vos utilisateurs iOS (Safari) et les utilisateurs Firefox ne bénéficient d'aucun gain. Le content negotiation via \u003Ccode>Vary: Accept\u003C/code> garantit qu'ils reçoivent le HTML classique, mais il n'y a aucun mécanisme de fallback pour le prefetch : ces navigateurs ne seront jamais prefetch depuis les SERP Google.\u003C/p>\n\u003Cp>Pour ces utilisateurs, les optimisations classiques de performance restent votre seul levier : compression Brotli, préconnexion DNS, réduction du critical path CSS, lazy loading agressif.\u003C/p>\n\u003Ch2>Monitoring en production et détection des régressions\u003C/h2>\n\u003Cp>Un SXG qui cesse de fonctionner silencieusement est pire qu'un SXG jamais déployé. Vous perdez le bénéfice de performance sans alerte, et les métriques CrUX se dégradent progressivement sur 28 jours avant que vous ne le remarquiez dans la Search Console.\u003C/p>\n\u003Ch3>Points de surveillance critiques\u003C/h3>\n\u003Cp>\u003Cstrong>Expiration du certificat SXG\u003C/strong> : 90 jours max. Si votre pipeline de renouvellement automatique (similaire à certbot pour Let's Encrypt) échoue, tous vos SXG deviennent invalides. Mettez une alerte à J-14.\u003C/p>\n\u003Cp>\u003Cstrong>Expiration des SXG individuels\u003C/strong> : 7 jours max. Si Web Packager est down pendant 7 jours, tous les SXG en cache expirent et Google cesse le prefetch. Monitorez le process Web Packager et le temps de réponse sur le port 8080.\u003C/p>\n\u003Cp>\u003Cstrong>Validité OCSP\u003C/strong> : l'agrafage OCSP doit être fonctionnel. Un répondeur OCSP down chez votre CA (DigiCert) rend vos SXG invalides le temps de la panne. Vous n'y pouvez rien, mais vous devez le détecter.\u003C/p>\n\u003Cp>\u003Cstrong>Header \u003Ccode>Content-Type\u003C/code>\u003C/strong> : la réponse SXG doit avoir exactement \u003Ccode>application/signed-exchange;v=b3\u003C/code>. Un reverse proxy mal configuré qui réécrit en \u003Ccode>text/html\u003C/code> casse tout.\u003C/p>\n\u003Cp>Un outil de monitoring comme Seogard détecte ces régressions automatiquement en surveillant les headers HTTP et les réponses pour chaque URL critique, évitant le scénario où un certificat expire un vendredi soir sans que personne ne s'en aperçoive.\u003C/p>\n\u003Ch3>Automatiser la vérification\u003C/h3>\n\u003Cp>Intégrez un check dans votre CI/CD ou votre cron de monitoring :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">#!/bin/bash\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># check-sxg-health.sh — À exécuter toutes les heures\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">DOMAIN\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"www.example-media.fr\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">TEST_URL\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"https://${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">DOMAIN\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}/article/politique-europeenne-2026\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># 1. Vérifier que le serveur répond en SXG\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">STATUS\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(\u003C/span>\u003Cspan style=\"color:#B392F0\">curl\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -s\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -o\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /dev/null\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -w\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"%{http_code}\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  -H\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Accept: application/signed-exchange;v=b3\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  \"${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">TEST_URL\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [ \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$STATUS\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> !=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"200\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ]; \u003C/span>\u003Cspan style=\"color:#F97583\">then\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"CRITICAL: SXG endpoint returned HTTP ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">STATUS\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}\"\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">    mail\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -s\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"SXG Health Check Failed\"\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> ops@example-media.fr\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  exit\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">fi\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># 2. Vérifier le Content-Type\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">CONTENT_TYPE\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(\u003C/span>\u003Cspan style=\"color:#B392F0\">curl\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -s\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -I\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  -H\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Accept: application/signed-exchange;v=b3\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  \"${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">TEST_URL\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\"> -i\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"content-type:\"\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> tr\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -d\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '\\r'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [[ \u003C/span>\u003Cspan style=\"color:#F97583\">!\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$CONTENT_TYPE\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> =~\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"signed-exchange\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ]]; \u003C/span>\u003Cspan style=\"color:#F97583\">then\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"CRITICAL: Wrong Content-Type: ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">CONTENT_TYPE\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  exit\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">fi\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># 3. Vérifier l'expiration du certificat SXG\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">CERT_EXPIRY\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(\u003C/span>\u003Cspan style=\"color:#B392F0\">openssl\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> x509\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -in\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /etc/webpkgserver/sxg-cert.pem\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  -noout\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -enddate\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> cut\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -d=\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -f2\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">EXPIRY_EPOCH\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(\u003C/span>\u003Cspan style=\"color:#B392F0\">date\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -d\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">CERT_EXPIRY\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}\"\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> +%s\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">NOW_EPOCH\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(\u003C/span>\u003Cspan style=\"color:#B392F0\">date\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> +%s\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">DAYS_LEFT\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(( (\u003C/span>\u003Cspan style=\"color:#B392F0\">EXPIRY_EPOCH\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> -\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> NOW_EPOCH\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#B392F0\">/\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 86400\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ))\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [ \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$DAYS_LEFT\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> -lt\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 14\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ]; \u003C/span>\u003Cspan style=\"color:#F97583\">then\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"WARNING: SXG cert expires in ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">DAYS_LEFT\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} days\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  exit\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">fi\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># 4. Vérifier l'endpoint cert-chain\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">CERT_STATUS\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(\u003C/span>\u003Cspan style=\"color:#B392F0\">curl\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -s\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -o\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /dev/null\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -w\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"%{http_code}\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  \"https://${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">DOMAIN\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}/.well-known/sxg-certs\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [ \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$CERT_STATUS\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> !=\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"200\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ]; \u003C/span>\u003Cspan style=\"color:#F97583\">then\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"CRITICAL: cert-chain endpoint returned HTTP ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">CERT_STATUS\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  exit\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">fi\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"OK: SXG healthy, cert expires in ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">DAYS_LEFT\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} days\"\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Ch3>Suivre l'impact dans vos KPIs\u003C/h3>\n\u003Cp>L'impact des SXG se mesure principalement via :\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>CrUX LCP\u003C/strong> segmenté par navigateur (Chrome vs autres) dans le \u003Ca href=\"/blog/mesurer-l-impact-seo-technique-quels-kpis-suivre\">rapport Page Experience de la Search Console\u003C/a>\u003C/li>\n\u003Cli>\u003Cstrong>CTR par position\u003C/strong> dans la Search Console — segmentez par device pour isoler le mobile Chrome\u003C/li>\n\u003Cli>\u003Cstrong>Nombre de SXG valides\u003C/strong> dans le rapport Search Console dédié\u003C/li>\n\u003Cli>Les \u003Ca href=\"/blog/search-console-api-automatiser-le-reporting-seo\">données de la Search Console API\u003C/a> pour automatiser le suivi des métriques de performance par groupe de pages SXG vs non-SXG\u003C/li>\n\u003C/ul>\n\u003Cp>Attention au biais de comparaison : ne comparez pas le LCP des pages SXG vs non-SXG directement, car vous avez probablement activé SXG sur vos pages les plus performantes d'abord. Utilisez un A/B test par échantillonnage d'URLs similaires, en respectant les bonnes pratiques d'\u003Ca href=\"/blog/a-b-testing-seo-tester-sans-penaliser-le-referencement\">A/B testing SEO\u003C/a>.\u003C/p>\n\u003Ch2>Quand NE PAS implémenter les SXG\u003C/h2>\n\u003Cp>Les SXG ne sont pas une silver bullet. Voici les cas où le ratio effort/bénéfice est défavorable :\u003C/p>\n\u003Cp>\u003Cstrong>Sites à faible trafic Google mobile Chrome\u003C/strong> : si votre audience est B2B desktop avec 40% de Firefox/Safari, le pool d'utilisateurs éligibles au prefetch SXG est trop petit pour justifier la complexité opérationnelle.\u003C/p>\n\u003Cp>\u003Cstrong>Sites entièrement personnalisés côté serveur\u003C/strong> : un SaaS dont chaque page SSR injecte des données utilisateur n'a aucune page éligible SXG sans refactoring majeur.\u003C/p>\n\u003Cp>\u003Cstrong>Stacks sans SSR\u003C/strong> : si votre site est une SPA React client-side rendue, le HTML servi est un shell vide avec un \u003Ccode>&#x3C;div id=\"root\">&#x3C;/div>\u003C/code>. Signer ce shell en SXG est inutile — le LCP dépend du JavaScript exécuté après, pas du temps de réception du HTML. Réglez d'abord votre problème de \u003Ca href=\"/blog/rendering-budget-de-google-combien-de-javascript-est-trop\">rendering budget\u003C/a> avant de penser aux SXG.\u003C/p>\n\u003Cp>\u003Cstrong>Infrastructure edge incompatible\u003C/strong> : si vous êtes sur un plan Cloudflare Free/Pro sans Workers, ou sur un hébergement mutualisé sans accès à la config Nginx, le déploiement de Web Packager est simplement impossible.\u003C/p>\n\u003Cp>Le meilleur candidat SXG : un site de contenu (média, e-commerce, documentation) avec du SSR propre, un trafic organique Google significatif, et une audience majoritairement Chrome mobile.\u003C/p>\n\u003Ch2>Wrap-up\u003C/h2>\n\u003Cp>Les Signed Exchanges éliminent la latence réseau entre le clic SERP et le premier byte — un gain mécanique de 300 à 800ms sur le LCP mobile que aucune optimisation front-end ne peut reproduire. Le coût d'entrée est réel (certificat SXG, Web Packager, content negotiation, monitoring), mais pour un site de contenu avec du trafic Google Chrome significatif, le ROI est mesurable en semaines. Mettez en place un monitoring continu des headers SXG et de l'expiration des certificats — un outil comme Seogard automatise cette surveillance — parce qu'un SXG cassé silencieusement est une régression de performance invisible jusqu'au prochain rapport CrUX.\u003C/p>\n\u003Cpre>\u003Ccode>\u003C/code>\u003C/pre>",null,12,[18,19,20,21,22],"sxg","signed-exchanges","performance","google","prefetch","Signed Exchanges (SXG) : prefetch instantané depuis Google","Sat Apr 11 2026 04:02:30 GMT+0000 (Coordinated Universal Time)",[26,41,54],{"_id":27,"slug":28,"__v":6,"author":7,"canonical":29,"category":10,"createdAt":30,"date":31,"description":32,"image":15,"imageAlt":15,"readingTime":16,"tags":33,"title":39,"updatedAt":40},"69d8e6d7aa6b273b0c603186","rendering-budget-de-google-combien-de-javascript-est-trop","https://seogard.io/blog/rendering-budget-de-google-combien-de-javascript-est-trop","2026-04-10T12:02:31.965Z","2026-04-10","Analyse technique des limites de rendering JavaScript de Googlebot : seuils, mesures concrètes et stratégies pour garder vos pages indexables.",[34,35,36,37,38],"rendering-budget","javascript","googlebot","limites","SEO technique","Rendering budget Google : combien de JS est trop pour Googlebot","Fri Apr 10 2026 12:02:31 GMT+0000 (Coordinated Universal Time)",{"_id":42,"slug":43,"__v":6,"author":7,"canonical":44,"category":10,"createdAt":45,"date":31,"description":46,"image":15,"imageAlt":15,"readingTime":16,"tags":47,"title":52,"updatedAt":53},"69d91f09aa6b273b0c8d2160","service-workers-et-seo-cache-offline-vs-crawlabilite","https://seogard.io/blog/service-workers-et-seo-cache-offline-vs-crawlabilite","2026-04-10T16:02:17.242Z","Comment les service workers impactent l'indexation Google. Stratégies de cache, pièges de crawlabilité et configurations pour concilier PWA et SEO.",[48,49,50,51,35],"service-worker","pwa","cache","seo","Service Workers et SEO : cache offline vs crawlabilité","Fri Apr 10 2026 16:02:17 GMT+0000 (Coordinated Universal Time)",{"_id":55,"slug":56,"__v":6,"author":7,"canonical":57,"category":10,"createdAt":58,"date":31,"description":59,"image":15,"imageAlt":15,"readingTime":16,"tags":60,"title":65,"updatedAt":66},"69d95741aa6b273b0cba1619","websocket-et-seo-contenu-temps-reel-et-indexation","https://seogard.io/blog/websocket-et-seo-contenu-temps-reel-et-indexation","2026-04-10T20:02:09.399Z","Comment gérer le contenu WebSocket pour qu'il soit crawlable et indexable. SSR, fallback HTTP, hydration : guide technique complet.",[61,62,51,63,64],"websocket","temps-réel","rendering","indexation","WebSocket et SEO : rendre le temps réel indexable","Fri Apr 10 2026 20:02:09 GMT+0000 (Coordinated Universal Time)"]