Retour à l'accueil

SSR pour SPA : configuration d'indexation

L'article explique comment configurer le rendu côté serveur pour l'indexation des applications SPA par les moteurs de recherche. Couvre Next.js, Nuxt.js, Angular Universal avec des exemples de code. Amélioration des Core Web Vitals et du trafic organique.

SSR pour l'indexation SPA : React, Vue, Angular
Advertisement 728x90

Rendu côté serveur pour les applications monopages (SPA) : résoudre l’indexation par les moteurs de recherche

Les robots d’exploration comme Googlebot ou YandexBot ne voient souvent qu’un HTML vide lorsqu’ils analysent des SPAs reposant sur le rendu côté client (CSR). Sans rendu côté serveur (SSR), ils n’attendent pas l’exécution du JavaScript — et laissent les pages non indexées. Le SSR résout ce problème en générant un HTML entièrement rendu côté serveur avant de l’envoyer au navigateur. Cela accélère le FCP, le LCP et le TTFB — améliorant ainsi les Core Web Vitals et la visibilité organique.

Comparaison des approches de rendu

| Paramètre | CSR | SSR | Pré-rendu | SSG |

|----------|-----|-----|--------------|-----|

Google AdInline article slot

| Lieu du rendu | Navigateur | Serveur (par requête) | Généré à l’avance pour des URL spécifiques | Généré à l’avance pour toutes les pages |

| FCP/LCP | Lent | Rapide | Rapide | Très rapide |

| SEO | Médiocre | Excellent | Bon | Excellent |

Google AdInline article slot

| Actualité des données | En temps réel | En temps réel | Obsolète | Obsolète |

| Charge serveur | Minimale | Élevée | Faible | Minimale |

Le SSR convient particulièrement aux pages dynamiques — catalogues produits, fils d’actualités, tableaux de bord. Le pré-rendu s’adapte mieux aux URL rarement mises à jour (ex. : pages marketing). La génération statique (SSG) est idéale pour les contenus fixes comme la documentation ou les pages d’accueil.

Google AdInline article slot

React + Next.js : mise en œuvre pas à pas

Créez un nouveau projet :

npx create-next-app@latest mon-app-ssr
cd mon-app-ssr
npm run dev

Pour les pages dynamiques, utilisez getServerSideProps :

// pages/product/[id].jsx
export async function getServerSideProps(context) {
  const { id } = context.params;
  const res = await fetch(`https://api.example.com/products/${id}`);
  const product = await res.json();

  if (!res.ok) {
    return { notFound: true };
  }

  return {
    props: { product },
  };
}

export default function ProductPage({ product }) {
  return (
    <main>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </main>
  );
}

Ajoutez des balises méta optimisées pour le référencement :

import Head from 'next/head';

export default function ProductPage({ product }) {
  return (
    <> 
      <Head>
        <title>{product.name} | Boutique</title>
        <meta name="description" content={product.shortDescription} />
        <meta property="og:title" content={product.name} />
        <meta property="og:image" content={product.imageUrl} />
      </Head>
      <main>
        <h1>{product.name}</h1>
      </main>
    </>
  );
}

Déploiement : Vercel, Netlify ou Node.js avec PM2.

Hydratation sans erreurs

L’hydratation désigne la façon dont React côté client « réanime » l’HTML rendu côté serveur. Évitez les incohérences :

  • Gardez les données côté serveur et côté client synchronisées.
  • Vérifiez l’environnement d’exécution avant d’utiliser window ou document.
  • Surveillez les avertissements dans la console navigateur.
const isBrowser = typeof window !== 'undefined';

Vue + Nuxt.js : rendu côté serveur

Initialisez un projet :

npx nuxi@latest init mon-app-nuxt
cd mon-app-nuxt
npm install
npm run dev

Récupérez les données côté serveur :

<!-- pages/product/[id].vue -->
<script setup>
const route = useRoute();
const { data: product, error } = await useFetch(
  `/api/products/${route.params.id}`
);

if (error.value) {
  throw createError({ statusCode: 404, message: 'Non trouvé' });
}

useHead({
  title: product.value?.name,
  meta: [
    { name: 'description', content: product.value?.shortDescription },
  ],
});
</script>

<template>
  <main>
    <h1>{{ product?.name }}</h1>
    <p>{{ product?.description }}</p>
  </main>
</template>

Configurez nuxt.config.ts :

export default defineNuxtConfig({
  ssr: true,
  app: {
    head: {
      htmlAttrs: { lang: 'fr' },
      link: [{ rel: 'canonical', href: 'https://example.com' }],
    },
  },
  nitro: {
    preset: 'node-server',
  },
});

Angular Universal : intégration SSR

Pour Angular 17+ :

ng add @angular/ssr

Définissez le point de terminaison serveur dans server.ts :

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
// ... autres imports

const server = express();
// ...

server.get('*', (req, res, next) => {
  commonEngine
    .render({
      bootstrap: AppServerModule,
      documentFilePath: join(browserDistFolder, 'index.html'),
      url: req.originalUrl,
      publicPath: browserDistFolder,
      providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
    })
    .then((html) => res.send(html))
    .catch(next);
});

Construisez et lancez :

npm run build:ssr
npm run serve:ssr

Détectez en toute sécurité la plateforme :

import { isPlatformBrowser } from '@angular/common';
import { inject, PLATFORM_ID } from '@angular/core';

const platformId = inject(PLATFORM_ID);

if (isPlatformBrowser(platformId)) {
  console.log(window.location.href);
}

Intégration avec un CMS headless

Pour Next.js + Contentful GraphQL :

export async function getServerSideProps({ params }) {
  const query = `
    query GetArticle($slug: String!) {
      articleCollection(where: { slug: $slug }) {
        items {
          title
          body
          seo {
            title
            description
          }
        }
      }
    }
  `;

  const res = await fetch(
    `https://graphql.contentful.com/content/v1/spaces/${process.env.CONTENTFUL_SPACE_ID}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.CONTENTFUL_TOKEN}`,
      },
      body: JSON.stringify({ query, variables: { slug: params.slug } }),
    }
  );

  const { data } = await res.json();
  // ...
}

Points clés à retenir

  • Le SSR fournit un HTML entièrement rendu aux robots d’exploration — résolvant ainsi le problème fondamental d’indexation des applications CSR.
  • Next.js, Nuxt.js et Angular Universal sont des frameworks éprouvés en production pour le SSR.
  • Les incohérences d’hydratation déclenchent des rendus complets — validez toujours l’environnement d’exécution.
  • Les Core Web Vitals (FCP, LCP, TTFB) s’améliorent de 50 à 80 % après passage au SSR.
  • Intégrez un cache Redis pour maintenir le TTFB sous les 200 ms même en cas de pics de trafic.

— Editorial Team

Advertisement 728x90

Lire ensuite