Le rendu côté serveur traditionnel sur des serveurs d'origine implique une latence moyenne de 200–400ms. Si vous mettez en cache le HTML sur un CDN edge, ce délai tombe à 20–50ms, mais vous perdez la personnalisation. Edge SSR brise ce compromis : vous obtenez à la fois la personnalisation et une réponse sous 40ms. Vous y parvenez avec des runtimes edge comme Cloudflare Workers et Vercel Edge, associés à un KV store distribué. Vous ne vous posez plus la question « cache ou personnalisation » — vous les obtenez tous les deux.

Pourquoi Edge SSR est critique maintenant

Depuis 2025, la métrique INP de Chrome est intégrée dans Core Web Vitals. Une réponse serveur supérieure à 200ms suffit à elle seule à casser INP. Chaque requête envoyée à l'origine ajoute 150–300ms en raison de la distance physique et du démarrage à froid. Edge runtime élimine ce goulot d'étranglement : le code s'exécute au POP (Point de Présence) le plus proche de l'utilisateur, et les données proviennent d'un KV store régional en 5–15ms.

Ce n'est pas qu'une question de vitesse. Pour la personnalisation, vous n'avez plus besoin de faire de requête à l'origine. Vous conservez les segments utilisateurs, les préférences et l'état du panier dans le KV edge. Lorsqu'une requête arrive, la fonction edge récupère ces données et rend le HTML instantanément. Le serveur d'origine n'est utilisé que pour les opérations d'écriture et les calculs intensifs.

Lorsque vous travaillez avec des plates-formes comme Shopify, cette architecture est particulièrement essentielle. Les templates Liquid se rendent à l'origine et prennent 300–600ms par page. Avec Edge SSR, vous rendez le HTML de manière composable : une fonction edge rend les cartes produit, une autre injecte les informations du panier. La latence totale reste sous les 40ms. Pour une intégration détaillée, consultez notre guide sur l'architecture Headless.

Cloudflare Workers + KV : le cœur de l'architecture

Cloudflare Workers fonctionne sur la base d'isolats V8. Au lieu de créer un nouveau conteneur pour chaque requête, il ouvre un isolat JavaScript. Son coût de démarrage est de 0,5–2ms. Le code Worker ressemble à ceci :

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const userId = request.headers.get('CF-Connecting-IP') || 'anonymous';
    
    // Récupérer le segment utilisateur depuis KV
    const segment = await env.USER_SEGMENTS.get(userId);
    
    // Rendre la liste produit selon le segment
    const products = segment === 'premium' 
      ? await fetchPremiumProducts() 
      : await fetchStandardProducts();
    
    const html = renderHTML(products, segment);
    
    return new Response(html, {
      headers: { 'Content-Type': 'text/html; charset=utf-8' }
    });
  }
};

Cloudflare KV se réplique sur plus de 300 POP. La latence de lecture moyenne globale est de 12ms. Les écritures se propagent avec la cohérence éventuelle en 60 secondes. C'est pourquoi vous écrivez dans KV uniquement les données qui changent rarement : préférences utilisateur, mappages de segments, feature flags. Pour les données qui changent fréquemment, comme les prix des produits, vous les récupérez auprès de l'API d'origine et les mettez en cache à l'edge (TTL de 60 secondes avec Cache API).

Vercel Edge vs Cloudflare Workers

Vercel Edge Functions utilise le même modèle d'isolat V8, mais avec un réseau différent. Cloudflare dispose de plus de 300 POP, Vercel d'environ 15 emplacements edge régionaux. Comparaison de latence (utilisateur en Europe, origine aux États-Unis) :

RuntimeDémarrage à froidLecture KVTTFB total
Origin SSR150msN/A380ms
Vercel Edge8ms22ms45ms
Cloudflare Workers1ms11ms28ms

L'avantage de Vercel réside dans son intégration approfondie avec l'écosystème Next.js. Vous écrivez une fonction edge dans middleware.ts et la déployez en production — l'orchestration se fait chez Vercel. Avec Cloudflare, vous avez besoin de Wrangler CLI et d'une liaison KV manuelle. Compromis : plus de contrôle versus un onboarding plus rapide.

Architecture KV store : pattern d'écriture et revalidation

La cohérence éventuelle du KV edge est une contrainte. Un utilisateur clique sur un bouton, la préférence change — ce changement se propage à tous les edges en 60 secondes. Pendant ce laps de temps, différents POP peuvent lire des valeurs différentes. Solution : rediriger après l'écriture ou implémenter une mise à jour optimiste côté client.

Flux exemple :

  1. L'utilisateur clique sur le toggle « Mode sombre »
  2. Le client envoie POST /api/preferences au serveur d'origine
  3. L'origine écrit user:123:theme = dark dans KV
  4. L'origine appelle l'API Cloudflare pour une invalidation de cache immédiate :
// Sur l'origine
await fetch(`https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`, {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${apiToken}` },
  body: JSON.stringify({ files: [`https://example.com/user/${userId}`] })
});
  1. La fonction edge lit la nouvelle valeur du KV à la requête suivante
  2. Le JavaScript côté client effectue un rechargement léger après 200ms

Ce pattern limite le débit d'écriture (limite de débit des écritures KV : 1000/seconde par compte), mais le débit de lecture est illimité. L'architecture est donc optimisée pour les charges de travail à lecture intensive. Les actions utilisateur sont rares (1–2 par minute), mais les pages vues sont fréquentes (100+ par seconde).

Stratégie de mise en cache en couches

KV n'est pas la seule couche de cache. Pile complète :

Cache navigateur (service worker)
  ↓
Cache CDN edge (Cache API, TTL 60s)
  ↓
Edge KV (éventuelle, minutes)
  ↓
Base de données d'origine

Les ressources statiques (CSS, JS) au sommet, les données spécifiques à l'utilisateur au bas. Le HTML lui-même est dans la couche intermédiaire : la fonction edge combine KV et Cache API pour le rendu. Pseudocode :

const cacheKey = `html:${url}:${segment}`;
let html = await caches.default.match(cacheKey);

if (!html) {
  const userData = await KV.get(userId);
  html = renderTemplate(userData);
  await caches.default.put(cacheKey, html, { expirationTtl: 60 });
}

return html;

Cette architecture maintient le percentile 95 du TTFB sous 40ms car la plupart des requêtes sont servies par Cache API (5–8ms). Le taux d'accès KV dépasse 98 %, le fallback d'origine reste sous 2 %.

Portée de personnalisation et compromis de taille de bundle

La fonction edge a une limite de taille de bundle de 1MB (Cloudflare). Vous ne pouvez pas rendre des composants React lourds. Deux stratégies :

1. Templating minimal : Utilisez Handlebars ou l'interpolation de chaîne personnalisée. Injectez simplement des variables :

const template = `<div class="product-card">
  <h3>{{name}}</h3>
  <span class="price {{priceClass}}">{{price}}</span>
</div>`;

function render(product, segment) {
  return template
    .replace('{{name}}', product.name)
    .replace('{{price}}', segment === 'premium' ? product.premiumPrice : product.price)
    .replace('{{priceClass}}', segment === 'premium' ? 'gold' : 'standard');
}

Taille de bundle : 2KB. Temps de rendu : 0,3ms.

2. Hydratation partielle : Rendez le HTML du squelette à l'edge, hydratez les îles React côté client. Fonction edge :

export default async function(request) {
  const products = await fetchProducts();
  return `
    <div id="product-list" data-products='${JSON.stringify(products)}'>
      ${products.map(p => `<div class="skeleton"></div>`).join('')}
    </div>
    <script type="module" src="/hydrate.js"></script>
  `;
}

Client-side hydrate.js (10KB) :

import { h, render } from 'preact';
const data = JSON.parse(document.getElementById('product-list').dataset.products);
render(<ProductList products={data} />, document.getElementById('product-list'));

Avec ce pattern, la latence d'Edge SSR reste faible (40ms), l'interactivité provient du client (FCP + 150ms). Compromis : INP peut augmenter (temps de parsing JavaScript). Une surveillance est nécessaire.

Monitoring utilisateur réel et alertes

Vous ne pouvez pas optimiser la latence edge sans RUM. Cloudflare Analytics ajoute un en-tête Server-Timing pour chaque requête :

Server-Timing: cf-edge;dur=12, cf-kv;dur=8, cf-render;dur=18

Collectez cela côté client avec PerformanceObserver :

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.entryType === 'navigation') {
      const ttfb = entry.responseStart - entry.requestStart;
      fetch('/analytics', { 
        method: 'POST', 
        body: JSON.stringify({ ttfb, url: entry.name }) 
      });
    }
  }
}).observe({ entryTypes: ['navigation'] });

Métriques cibles :

  • p50 TTFB < 30ms
  • p95 TTFB < 60ms
  • p99 TTFB < 100ms
  • Taux d'erreur edge < 0,1%

Pour les requêtes dépassant 60ms, consignez l'ID de trace Cloudflare et déboguez avec Wrangler tail. Le plus souvent, la cause est un timeout KV ou un fallback d'origine.

Checklist de déploiement en production

Avant de mettre Edge SSR en production :

  1. Rate limiting : Limitez les écritures KV (1 écriture par seconde par utilisateur)
  2. Chaîne de fallback : Si le timeout KV dépasse 50ms, basculez à l'origine ; si l'origine expire, servez du HTML statique
  3. Feature flag : Déployez progressivement la personnalisation edge (10 % → 50 % → 100 % du trafic)
  4. Monitoring des coûts : Cloudflare Workers offre 100K requêtes/jour gratuitement, puis $0,50/million. Lectures KV illimitées gratuitement, écritures à $0,50/million.
  5. Sécurité : Hashifiez l'ID utilisateur, ne conservez pas d'informations personnelles dans les clés KV, ajoutez la détection de bots pour contourner les limites de débit

Projection de coûts : 1M visites quotidiennes, 30 % de requêtes personnalisées = 300K invocations edge/jour = $0,15/jour = $4,50/mois. L'alternative SSR d'origine : instance 2 vCPU à $50/mois. Économies : 91 %.

Une fois l'architecture Edge SSR en place, le coût supplémentaire est quasi nul. Ajouter une nouvelle règle de personnalisation signifie simplement écrire une nouvelle clé dans KV. Créer un nouveau segment revient à ajouter un bloc if dans la fonction edge. La mise à l'échelle n'est pas linéaire mais logarithmique — 10M requêtes/jour sont servies avec la même latence de 40ms. C'est pourquoi intégrer une stratégie edge-first dès le départ procure un avantage considérable pour la croissance future.