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.
Ce que sont réellement les Signed Exchanges
Un 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.
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.
Le mécanisme de prefetch dans les SERP
Quand Google détecte qu'une page de résultats contient des liens vers des sites servant des SXG valides, il peut injecter un <link 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.
Ce comportement est documenté par Google dans la spécification Web Packaging 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.
Différence avec AMP et pourquoi SXG l'a remplacé
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.
Prérequis techniques et obtention du certificat
L'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.
Le certificat SXG
Un 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 pour les cas simples.
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.
Pour générer une clé privée et une CSR (Certificate Signing Request) compatibles :
# Générer la clé privée ECDSA P-256 (requise pour SXG)
openssl ecparam -genkey -name prime256v1 -out sxg-key.pem
# Générer la CSR avec l'extension CanSignHttpExchanges
openssl req -new -key sxg-key.pem -out sxg.csr \
-subj "/CN=www.example-media.fr" \
-addext "1.3.6.1.4.1.11129.2.1.22=DER:05:00"
# Vérifier que l'extension est présente dans la CSR
openssl req -in sxg.csr -text -noout | grep -A1 "1.3.6.1.4.1.11129"
Remplacez www.example-media.fr par votre domaine réel. L'algorithme ECDSA P-256 est obligatoire — RSA n'est pas accepté pour SXG.
Web Packager Server
Google maintient Web Packager Server, 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.
Voici une configuration webpkgserver.toml réaliste pour un site média :
# /etc/webpkgserver/webpkgserver.toml
[Listen]
# Port d'écoute du serveur SXG
Host = "127.0.0.1"
Port = 8080
# TLS géré par le reverse proxy amont (Nginx)
[Server]
# Domaine pour lequel on signe les exchanges
CertURLBase = "https://www.example-media.fr"
# Durée de validité des SXG (max 7 jours = 604800 sec)
ExchangeLifetime = 259200 # 3 jours — marge avant expiration
[SXG]
# Chemin vers le certificat SXG (pas le TLS classique)
CertPath = "/etc/webpkgserver/sxg-cert.pem"
KeyPath = "/etc/webpkgserver/sxg-key.pem"
# OCSP stapling (obligatoire pour SXG)
OCSPStaple = true
[Sign]
# URLs à signer — regex des chemins éligibles
URLPattern = "https://www.example-media.fr/article/.*"
# Exclure les pages avec contenu personnalisé
# (les SXG ne supportent pas le contenu dynamique par utilisateur)
[Fetch]
# Timeout pour récupérer le contenu depuis l'origine
Timeout = 10
# Headers à forward
AllowedRequestHeaders = ["Accept-Language"]
Le 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.
Configuration Nginx pour le content negotiation
Le 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.
# /etc/nginx/conf.d/sxg.conf
upstream webpkgserver {
server 127.0.0.1:8080;
}
upstream origin {
server 127.0.0.1:3000; # Votre app Next.js / Nuxt / autre
}
server {
listen 443 ssl http2;
server_name www.example-media.fr;
ssl_certificate /etc/letsencrypt/live/www.example-media.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example-media.fr/privkey.pem;
# Content negotiation pour SXG
location /article/ {
# Vérifier si le client accepte SXG
if ($http_accept ~* "application/signed-exchange;v=b3") {
# Ajouter le header Vary pour les caches intermédiaires
add_header Vary "Accept" always;
# Proxy vers Web Packager Server
proxy_pass http://webpkgserver;
break;
}
# Contenu HTML classique pour les autres clients
add_header Vary "Accept" always;
proxy_pass http://origin;
}
# Endpoint pour le certificat SXG (Google le récupère)
location /.well-known/sxg-certs {
alias /etc/webpkgserver/cert-chain/;
types {
application/cert-chain+cbor cbor;
}
add_header Access-Control-Allow-Origin "*";
add_header Cache-Control "public, max-age=86400";
}
# Le reste du site — pas de SXG
location / {
proxy_pass http://origin;
}
}
Trois détails à ne pas rater :
-
Le header
Vary: Acceptest 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. -
L'endpoint
/.well-known/sxg-certssert 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. -
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 ocspque votre répondeur OCSP est accessible.
Scénario concret : un média en ligne de 8 000 pages
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.
Situation avant SXG
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.
Dans la Search Console, les données Page Experience montrent que 61% des pages passent le seuil "Good" pour le LCP (< 2.5s). Les 39% restantes sont pénalisées par la latence réseau, surtout sur les connexions 4G lentes.
Déploiement SXG
L'é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.
Le pipeline :
- Next.js rend le HTML en SSR (comme avant)
- Web Packager intercepte les requêtes avec
Accept: application/signed-exchange;v=b3 - Il récupère le HTML depuis l'origine locale (127.0.0.1:3000), le signe, et le renvoie
- Cloudflare cache le SXG avec
Vary: Accept(cache key distincte du HTML) - Google crawle les SXG, les valide, et les stocke dans son cache de prefetch
Validation et debugging
Avant de déployer en production, validez vos SXG avec l'outil en ligne de commande dump-signedexchange :
# Installer l'outil Go
go install github.com/nicholasgasior/webpkg/cmd/dump-signedexchange@latest
# Télécharger un SXG depuis votre serveur
curl -s -H "Accept: application/signed-exchange;v=b3" \
"https://www.example-media.fr/article/politique-europeenne-2026" \
-o test.sxg
# Valider le SXG
dump-signedexchange -i test.sxg -verify
# Sortie attendue :
# The exchange is valid.
# URI: https://www.example-media.fr/article/politique-europeenne-2026
# Status: 200
# Signature expires: 2026-04-14T...
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 chrome://web-package-internals/ pour le debugging avancé.
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 Vary manquant, taille du SXG dépassant 8 Mo.
Résultats mesurés
Après 6 semaines de déploiement progressif (d'abord 500 articles, puis l'ensemble du catalogue) :
- LCP P75 mobile depuis les SERP : 2.8s → 1.7s (réduction de ~39%)
- Pages "Good" LCP : 61% → 84%
- FID / INP : inchangé (SXG n'affecte que le chargement initial, pas l'interactivité post-load)
- 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)
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.
Edge cases et limitations à connaître
Contenu personnalisé : l'incompatibilité fondamentale
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.
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.
Headers interdits
La 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.
Impact sur le crawl budget
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 URLPattern de Web Packager.
Le lien avec la gestion du rendering budget 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.
Interaction avec le CDN et le cache edge
Si vous utilisez un CDN qui fait de la modification des réponses HTTP au niveau edge, 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 :
- Signer au niveau edge (Cloudflare Workers avec la lib
sxg-rsde Google) - Signer à l'origine et interdire toute modification edge sur les réponses SXG
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.
Navigateurs non supportés
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 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.
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.
Monitoring en production et détection des régressions
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.
Points de surveillance critiques
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.
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.
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.
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.
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.
Automatiser la vérification
Intégrez un check dans votre CI/CD ou votre cron de monitoring :
#!/bin/bash
# check-sxg-health.sh — À exécuter toutes les heures
DOMAIN="www.example-media.fr"
TEST_URL="https://${DOMAIN}/article/politique-europeenne-2026"
# 1. Vérifier que le serveur répond en SXG
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Accept: application/signed-exchange;v=b3" \
"${TEST_URL}")
if [ "$STATUS" != "200" ]; then
echo "CRITICAL: SXG endpoint returned HTTP ${STATUS}" | \
mail -s "SXG Health Check Failed" [email protected]
exit 1
fi
# 2. Vérifier le Content-Type
CONTENT_TYPE=$(curl -s -I \
-H "Accept: application/signed-exchange;v=b3" \
"${TEST_URL}" | grep -i "content-type:" | tr -d '\r')
if [[ ! "$CONTENT_TYPE" =~ "signed-exchange" ]]; then
echo "CRITICAL: Wrong Content-Type: ${CONTENT_TYPE}"
exit 1
fi
# 3. Vérifier l'expiration du certificat SXG
CERT_EXPIRY=$(openssl x509 -in /etc/webpkgserver/sxg-cert.pem \
-noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "${CERT_EXPIRY}" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -lt 14 ]; then
echo "WARNING: SXG cert expires in ${DAYS_LEFT} days"
exit 1
fi
# 4. Vérifier l'endpoint cert-chain
CERT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"https://${DOMAIN}/.well-known/sxg-certs")
if [ "$CERT_STATUS" != "200" ]; then
echo "CRITICAL: cert-chain endpoint returned HTTP ${CERT_STATUS}"
exit 1
fi
echo "OK: SXG healthy, cert expires in ${DAYS_LEFT} days"
Suivre l'impact dans vos KPIs
L'impact des SXG se mesure principalement via :
- CrUX LCP segmenté par navigateur (Chrome vs autres) dans le rapport Page Experience de la Search Console
- CTR par position dans la Search Console — segmentez par device pour isoler le mobile Chrome
- Nombre de SXG valides dans le rapport Search Console dédié
- Les données de la Search Console API pour automatiser le suivi des métriques de performance par groupe de pages SXG vs non-SXG
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'A/B testing SEO.
Quand NE PAS implémenter les SXG
Les SXG ne sont pas une silver bullet. Voici les cas où le ratio effort/bénéfice est défavorable :
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.
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.
Stacks sans SSR : si votre site est une SPA React client-side rendue, le HTML servi est un shell vide avec un <div id="root"></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 avant de penser aux SXG.
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.
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.
Wrap-up
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.