En PrestaShop headless, choisir entre SSR et CSR n'est pas qu'une décision d'architecture : c'est une décision SEO. Un composant rendu côté client laisse une page blanche à Googlebot pendant que vos concurrents servent du HTML complet. Voici comment mesurer l'impact réel et éviter les pièges d'indexation.
SSR vs CSR : rappel des mécanismes
En architecture headless, le navigateur reçoit soit du HTML déjà peuplé (SSR/SSG), soit un squelette HTML que JavaScript doit hydrater (CSR). La différence est critique pour Google :
- SSR (Server-Side Rendering) : Next.js exécute le composant sur le serveur, Googlebot reçoit le HTML complet dès le premier octet
- SSG (Static Site Generation) : HTML pré-généré au build, servi depuis le CDN, temps de réponse optimal
- ISR (Incremental Static Regeneration) : SSG avec revalidation en arrière-plan, le meilleur compromis pour les catalogues
- CSR (Client-Side Rendering) : page vide jusqu'à exécution du JS, risque d'indexation partielle ou nulle
Google déclare indexer le contenu rendu côté client, mais c'est une seconde vague de crawl différée, parfois de plusieurs jours. Pour un catalogue e-commerce où le stock et les prix changent quotidiennement, ce délai est inacceptable.
Comment Googlebot voit votre page headless
L'outil le plus simple pour diagnostiquer : Google Search Console → Inspecter l'URL. La capture d'écran "Page rendue" montre exactement ce que Googlebot a vu lors du dernier crawl.
Pour tester en local sans GSC, vous pouvez simuler un crawler sans JS via curl :
# Ce que voit un crawler sans JavaScript
curl -A "Googlebot" https://votre-shop.com/produit/mon-produit \
| grep -E '(title|description|price|h1)'
# Si la sortie est vide → votre contenu est CSR-only, problème SEO critique
Dans Next.js, vérifiez quel type de rendu s'applique à chaque page avec les indicateurs de build :
# npm run build affiche le type de rendu pour chaque route
Route (app) Size First Load JS
┌ ○ / 1.2 kB 87.4 kB
├ ● /produit/[slug] 3.1 kB 102 kB ← SSG/ISR ✅
├ λ /panier 2.8 kB 98.7 kB ← SSR ✅
└ ƒ /compte/commandes 1.9 kB 91.2 kB ← Dynamic (SSR) ✅
# Légende : ○ static ● SSG λ SSR ƒ dynamic
Les pages critiques et leur stratégie de rendu
Toutes les pages d'une boutique n'ont pas le même besoin de rendu serveur. Voici la matrice à appliquer :
Pages à indexer absolument → SSG + ISR
// app/produit/[slug]/page.tsx
// ISR : revalide toutes les heures sans bloquer le crawl
export const revalidate = 3600;
export async function generateStaticParams() {
const products = await fetchProducts({ active: true, limit: 5000 });
return products.map((p) => ({ slug: p.link_rewrite }));
}
export default async function ProductPage({ params }: { params: { slug: string } }) {
// Fetché côté serveur → dans le HTML initial
const product = await getProductBySlug(params.slug);
return <ProductDetail product={product} />;
}
Pages avec données temps réel → SSR strict
// app/catalogue/[category]/page.tsx
// Pas de revalidate → SSR à chaque requête (prix, stocks live)
export const dynamic = 'force-dynamic';
export default async function CategoryPage({ params, searchParams }) {
// searchParams (tri, filtre) force le SSR de toute façon
const products = await getProductsByCategory(params.category, searchParams);
return <ProductGrid products={products} />;
}
Pages jamais indexées → CSR autorisé
// Compte client, panier, tunnel de commande
// Ces pages doivent être bloquées dans robots.txt
'use client';
export default function CartPage() {
const [cart, setCart] = useCartStore();
// CSR acceptable ici : Google ne doit pas indexer /panier
return <CartSummary cart={cart} />;
}
Le piège du "use client" qui remonte trop haut
Le problème le plus fréquent en Next.js App Router : placer 'use client' sur un composant parent qui contient du contenu SEO-critique, transformant tout l'arbre en CSR :
// ❌ ProductDetail est 'use client' → tout le contenu devient CSR
'use client';
export default function ProductDetail({ product }) {
const [quantity, setQuantity] = useState(1);
return (
<div>
<h1>{product.name}</h1> {/* ← Google ne voit pas ça ! */}
<p>{product.description}</p> {/* ← ni ça */}
<QuantitySelector value={quantity} onChange={setQuantity} />
</div>
);
}
// ✅ Isoler le composant interactif, garder le contenu SEO côté serveur
// components/ProductDetail.tsx (Server Component, pas de 'use client')
import { QuantitySelector } from './QuantitySelector'; // client component
export default function ProductDetail({ product }) {
return (
<div>
<h1>{product.name}</h1> {/* ← rendu serveur ✅ */}
<p>{product.description}</p> {/* ← rendu serveur ✅ */}
<QuantitySelector productId={product.id} /> {/* client isolé */}
</div>
);
}
// components/QuantitySelector.tsx
'use client';
export function QuantitySelector({ productId }) {
const [quantity, setQuantity] = useState(1);
// interactivité CSR uniquement ici
}
Mesurer l'impact SEO : métriques à surveiller
Après une migration ou refacto du rendu, suivez ces signaux dans Google Search Console :
- Couverture → Pages indexées : doit augmenter après passage au SSR/SSG
- Pages exclues "découvertes non indexées" : souvent causées par CSR + timeout du crawler
- Inspection d'URL → HTML rendu vs HTML téléchargé : comparer le contenu des deux
- Core Web Vitals LCP : le SSG servi via CDN améliore significativement le LCP
Côté performance pure, benchmark simple pour quantifier le gain :
# Mesure TTFB avec SSR vs CDN (SSG)
curl -w "TTFB: %{time_starttransfer}s\n" -o /dev/null -s \
https://shop.example.com/produit/mon-produit
# Avec SSG + CDN : ~50-100ms
# Avec SSR origine : ~300-800ms selon la charge PS
# Avec CSR : TTFB bas mais FCP élevé (JS à exécuter)
Configuration robots.txt pour les pages CSR
Les pages en CSR pur (panier, compte, checkout) doivent être explicitement bloquées pour éviter que Google gaspille son crawl budget sur du contenu vide :
// app/robots.ts
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: [
'/panier',
'/compte/',
'/commande/',
'/paiement/',
],
},
],
sitemap: 'https://shop.example.com/sitemap.xml',
};
}
Checklist avant mise en production
- Toutes les pages produit et catégorie utilisent SSG + ISR (pas de
'use client'au niveau page) - Les composants interactifs (
'use client') sont feuilles de l'arbre, jamais racines - Le build Next.js montre
●ouλpour les routes SEO-critiques, jamaisƒsans intention - Test curl sans JS sur les 5 pages les plus importantes : titre, prix, description présents
- Robots.txt bloque toutes les pages CSR non-indexables
- Google Search Console configurée et première inspection d'URL validée
Conclusion
En PrestaShop headless avec Next.js, le SSR et le SSG ne sont pas des optimisations optionnelles — ils sont la condition sine qua non d'une indexation correcte. La règle d'or : tout contenu que Google doit indexer doit être dans le HTML initial, sans attendre JavaScript. Les Server Components de l'App Router rendent ça naturel, à condition de ne pas laisser 'use client' contaminer les composants parents.
Vous avez des doutes sur le rendu de votre front headless ? Contactez-moi pour un audit rapide de votre architecture.