PrestaShop headless avec Next.js : architecture SEO-friendly

PrestaShop headless avec Next.js : architecture SEO-friendly
Découpler le front PrestaShop avec Next.js offre un contrôle SEO total — à condition d'adopter la bonne architecture. SSR, ISR, sitemap dynamique, structured data : le guide complet.

Découpler le front PrestaShop avec Next.js améliore drastiquement les performances et le contrôle SEO — à condition d'adopter la bonne architecture dès le départ. Ce guide couvre le câblage API, le rendu SSR/ISR et les points critiques pour que Google indexe correctement votre catalogue.

Pourquoi headless PrestaShop avec Next.js ?

PrestaShop expose depuis la version 1.7 une API REST complète (Webservices) et PrestaShop 9 introduit une API modulaire Symfony plus cohérente. Next.js, de son côté, combine le meilleur du rendu côté serveur (SSR) et de la génération statique (SSG/ISR), ce qui en fait un choix naturel pour les fronts e-commerce SEO-friendly.

Les bénéfices concrets de cette architecture :

  • Performance : HTML pré-rendu, pas de JavaScript à exécuter avant le premier affichage
  • SEO total : contrôle absolu des balises <head>, canonical, structured data, sitemap
  • DX : stack React/TypeScript, hot reload, écosystème npm
  • Scalabilité : front déployable sur Vercel/CDN, back PrestaShop isolé

Architecture cible

L'architecture se divise en deux domaines distincts :

┌─────────────────────────────────────────────────┐
│  Next.js (front)         │  PrestaShop (back)    │
│  shop.example.com        │  api.example.com      │
│                          │                       │
│  Pages SSR/ISR           │  Webservices REST      │
│  /catalogue/[slug]       │  /api/products        │
│  /produit/[slug]         │  /api/categories      │
│                          │  /api/orders          │
└─────────────────────────────────────────────────┘

Le front Next.js ne sert jamais de proxy — il consomme directement l'API PrestaShop côté serveur (dans les Server Components ou getServerSideProps). Le panier et le checkout peuvent rester sur PrestaShop natif ou être réécrits en React selon le budget.

Activer et sécuriser l'API PrestaShop

Dans Paramètres avancés → Webservices, activez l'API et créez une clé avec les permissions nécessaires en lecture :

// Ressources minimales pour un front catalogue
products        → GET
categories      → GET
product_images  → GET
combinations    → GET
stock_availables → GET

Côté Next.js, stockez la clé dans les variables d'environnement et créez un client HTTP dédié :

// lib/prestashop-client.ts
const PS_API_URL = process.env.PS_API_URL!;
const PS_API_KEY = process.env.PS_API_KEY!;

export async function fetchPs<T>(resource: string, params: Record<string, string> = {}): Promise<T> {
  const url = new URL(`${PS_API_URL}/api/${resource}`);
  url.searchParams.set('output_format', 'JSON');
  url.searchParams.set('display', 'full');

  for (const [key, value] of Object.entries(params)) {
    url.searchParams.set(key, value);
  }

  const response = await fetch(url.toString(), {
    headers: {
      Authorization: `Basic ${Buffer.from(`${PS_API_KEY}:`).toString('base64')}`,
    },
    next: { revalidate: 3600 }, // ISR : revalide toutes les heures
  });

  if (!response.ok) {
    throw new Error(`PS API error: ${response.status} on ${resource}`);
  }

  return response.json() as Promise<T>;
}

L'option next: { revalidate: 3600 } active l'Incremental Static Regeneration : Next.js met la réponse en cache et la régénère en arrière-plan toutes les heures, sans bloquer les utilisateurs.

Page produit SEO-friendly avec SSR

Pour les fiches produit, le SSR garantit que Google reçoit le HTML complet à chaque crawl, même si les données changent souvent (stock, prix) :

// app/produit/[slug]/page.tsx
import { fetchPs } from '@/lib/prestashop-client';
import type { Metadata } from 'next';

interface ProductPageProps {
  params: { slug: string };
}

export async function generateMetadata({ params }: ProductPageProps): Promise<Metadata> {
  const product = await getProductBySlug(params.slug);

  return {
    title: `${product.name} | Ma Boutique`,
    description: product.description_short,
    openGraph: {
      title: product.name,
      description: product.description_short,
      images: [{ url: product.cover_image_url, width: 800, height: 800 }],
    },
    alternates: {
      canonical: `https://shop.example.com/produit/${params.slug}`,
    },
  };
}

export default async function ProductPage({ params }: ProductPageProps) {
  const product = await getProductBySlug(params.slug);

  const structuredData = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    description: product.description_short,
    image: product.cover_image_url,
    offers: {
      '@type': 'Offer',
      price: product.price,
      priceCurrency: 'EUR',
      availability: product.quantity > 0
        ? 'https://schema.org/InStock'
        : 'https://schema.org/OutOfStock',
    },
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
      />
      <ProductDetail product={product} />
    </>
  );
}

Génération statique du catalogue avec ISR

Pour les pages de catégorie et le listing produits, l'ISR est idéal : le contenu est statique 99 % du temps mais peut être régénéré automatiquement :

// app/catalogue/[category]/page.tsx
export async function generateStaticParams() {
  const data = await fetchPs<{ categories: Array<{ link_rewrite: string }> }>('categories', {
    filter: '[active]': '1',
  });

  return data.categories.map((category) => ({
    category: category.link_rewrite,
  }));
}

// Revalidation toutes les 2 heures
export const revalidate = 7200;

Next.js pré-génère toutes les pages de catégorie au build, puis les régénère en arrière-plan selon l'intervalle défini.

Sitemap dynamique

Next.js 13+ permet de générer un sitemap programmatiquement via un fichier app/sitemap.ts :

// app/sitemap.ts
import { MetadataRoute } from 'next';
import { fetchPs } from '@/lib/prestashop-client';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const BASE_URL = 'https://shop.example.com';

  const productsData = await fetchPs<{ products: Array<{ link_rewrite: string; date_upd: string }> }>(
    'products',
    { filter: '[active]': '1', limit: '1000' }
  );

  const productUrls = productsData.products.map((product) => ({
    url: `${BASE_URL}/produit/${product.link_rewrite}`,
    lastModified: new Date(product.date_upd),
    changeFrequency: 'weekly' as const,
    priority: 0.8,
  }));

  return [
    { url: BASE_URL, lastModified: new Date(), changeFrequency: 'daily', priority: 1 },
    ...productUrls,
  ];
}

Pièges courants à éviter

Quelques erreurs fréquentes rencontrées sur des projets headless PrestaShop :

  • Utiliser useEffect pour fetch les données produit : le contenu n'est pas dans le HTML initial, Google voit une page vide. Toujours fetcher dans les Server Components.
  • Oublier les balises canonical : si PrestaShop reste accessible en direct (api.example.com/fr/produit/…), les deux URLs se font concurrence. Ajoutez des canonical côté PS ou bloquez avec robots.txt.
  • Exposer la clé API en frontend : toutes les requêtes à l'API PS doivent passer côté serveur. Ne jamais mettre PS_API_KEY dans NEXT_PUBLIC_.
  • Ignorer le cache du panier : l'état du panier est côté client (localStorage ou session cookie). Il faut gérer la synchronisation avec l'API PrestaShop cart séparément.

Checklist SEO pour go-live

  • Canonical sur toutes les pages (produit, catégorie, pagination)
  • Balises hreflang si boutique multilingue
  • Structured data Product + BreadcrumbList + Organization
  • Sitemap accessible sur /sitemap.xml et soumis à Google Search Console
  • robots.txt bloquant l'accès direct au back PrestaShop
  • Core Web Vitals : LCP < 2,5 s, CLS = 0, INP < 200 ms

Conclusion

Une architecture headless PrestaShop + Next.js bien construite offre un contrôle SEO total que le front natif PrestaShop ne peut pas atteindre. La clé est de toujours rendre côté serveur les données critiques pour l'indexation, de gérer précisément les canonical et les métadonnées, et de ne jamais exposer la logique de fetch côté client.

Vous construisez un front headless ou migrez une boutique existante ? Contactez-moi pour qu'on en discute.

Jonathan Le-Peru

Écrit par Jonathan Le-Peru

Développeur backend avec plus de 7 ans d'expérience, spécialisé dans la création de solutions e-commerce robustes avec Prestashop. Passionné par l'optimisation des performances et les bonnes pratiques de développement.