Dans les sites e-commerce modernes, la personnalisation est devenue une attente — mais aucun utilisateur ne veut attendre 250ms à chaque interaction. L'architecture SSR (server-side rendering) traditionnelle génère une latence moyenne de 150–300ms entre l'utilisateur et le serveur d'origine : recherche DNS, handshake TCP, négociation TLS, temps de traitement à l'origine. Edge SSR réduit ce délai à 40–60ms en exploitant la proximité géographique et une KV store globale. Des plateformes comme Cloudflare Workers et Vercel Edge Functions offrent un runtime au bord du réseau ; notre travail consiste à transférer la logique de personnalisation vers cet endroit et à configurer correctement la KV store.
Différence de latence : Edge SSR vs. SSR d'origine
Dans l'architecture SSR classique, la requête suit ce chemin : utilisateur → CDN (cache miss) → serveur d'origine (requête DB + rendu) → réponse. Latence totale moyenne : 250ms, percentile 95 : 450ms. Avec Edge SSR, la requête se termine à une localité edge : utilisateur → worker edge (lookup KV + rendu) → réponse. Moyenne : 40ms, percentile 95 : 80ms.
Sources de latence :
| Étape | SSR d'origine | Edge SSR |
|---|---|---|
| DNS + TLS | 50ms | 15ms (proximité edge) |
| RTT réseau | 120ms (intercontinental) | 10ms (distance edge) |
| Calcul | 80ms (origine) | 15ms (isolate V8) |
| Total | 250ms | 40ms |
Cette réduction de 84% impacte directement les métriques LCP (Largest Contentful Paint) et CLS (Cumulative Layout Shift). Selon le rapport 2025 de Google sur Core Web Vitals, chaque 100ms sur LCP entraîne une augmentation de %3.5 du taux de rebond — gagner 210ms représente un lift de conversion de %7.3 (calcul : 210/100 × 3.5).
Compromis : le runtime edge n'est pas Node.js, c'est un isolate V8 — pas de modules natifs, pas de système de fichiers, pas de processus enfants. La logique de personnalisation doit être entièrement sans état et légère.
Architecture Edge SSR avec Cloudflare Workers
Cloudflare Workers achemine chaque requête vers l'une de ses 300+ locations edge à travers le réseau mondial. Une requête est traitée à l'edge comme suit :
// worker.js — Cloudflare Workers
export default {
async fetch(request, env) {
const url = new URL(request.url);
const userId = request.headers.get('x-user-id'); // parsé depuis JWT
// Récupérer le segment utilisateur depuis KV
const segment = await env.USER_SEGMENTS.get(userId);
const prefs = segment ? JSON.parse(segment) : { tier: 'free' };
// Rendre HTML personnalisé
const html = renderHTML(prefs, url.pathname);
return new Response(html, {
headers: {
'content-type': 'text/html;charset=UTF-8',
'cache-control': 'public, s-maxage=60', // cache edge 60s
},
});
},
};
function renderHTML(prefs, path) {
const hero = prefs.tier === 'premium'
? '<h1>Contenu Premium</h1>'
: '<h1>Contenu Gratuit</h1>';
return `<!DOCTYPE html><html><body>${hero}<p>Chemin : ${path}</p></body></html>`;
}
À chaque requête, ce code extrait le segment utilisateur de l'espace de noms USER_SEGMENTS dans KV. La latence de lecture KV est en moyenne 15ms à l'échelle mondiale (benchmark Cloudflare 2025). Alternativement, on peut utiliser Durable Objects, mais pour les charges de travail read-heavy, KV est plus économique (KV : $0.50/million de lectures, DO : $0.15/million de requêtes + calcul).
La limite de temps CPU pour Workers est 50ms — les rendus complexes peuvent la dépasser. La solution : pré-rendre les modèles sous forme HTML et les stocker dans KV ; le worker ne fait que remplacer des variables. Par exemple, le worker remplace le placeholder {USER_NAME}, tandis que le modèle est stocké dans KV.
Intégration Edge Functions Vercel + Middleware Next.js
Vercel Edge Functions s'intègrent nativement à Next.js 13+ — vous pouvez intercepter les requêtes via le pattern middleware et les personnaliser. Au lieu de getServerSideProps dans le runtime edge, vous utilisez middleware.ts :
// middleware.ts — Vercel Edge
import { NextRequest, NextResponse } from 'next/server';
export async function middleware(req: NextRequest) {
const userId = req.cookies.get('user_id')?.value;
if (!userId) return NextResponse.next();
// Récupérer le segment depuis Edge Config (équivalent KV Vercel)
const segment = await fetch(`https://edge-config.vercel.com/${userId}`).then(r => r.json());
// Ajouter les infos de segment en en-tête, lues par le composant page
const response = NextResponse.next();
response.headers.set('x-user-segment', segment.tier);
return response;
}
export const config = {
matcher: ['/product/:path*', '/category/:path*'],
};
Cette approche fonctionne bien pour personnaliser les pages de listing de produits dans une architecture headless commerce. Par exemple, vous montrez un classement de produits différent aux utilisateurs premium. Le composant page lit l'en-tête :
// app/product/[id]/page.tsx
export default async function ProductPage({ params, headers }) {
const segment = headers.get('x-user-segment');
const products = await fetchProducts(params.id, segment);
return <ProductList items={products} />;
}
Vercel Edge Config réplique globalement en moins de 150ms — les mises à jour KV se propagent aux edges dans ce laps de temps. Compromis : Edge Config est environ 20% plus lent que Cloudflare KV, mais mieux intégré à l'écosystème Next.js.
Architecture KV Store : stratégie de segmentation
Les données de personnalisation sont stockées dans KV sur trois couches :
- Segment utilisateur :
USER_SEGMENTS:{userId}→{"tier":"premium","region":"EU"} - Config de segment :
SEGMENT_CONFIG:{tier}→{"discount":0.2,"hero":"premium.jpg"} - Modèle de page :
PAGE_TPL:{page}:{tier}→ fragment HTML pré-rendu
Cette structure assure que lors d'un changement de segment, seul USER_SEGMENTS est mis à jour ; les modèles restent en cache. Pour 1 million d'utilisateurs, le coût KV est : 1M utilisateurs × 1 lecture/requête × $0.50/1M lectures = $0.0000005 par requête. Le coût d'une requête à la base de données d'origine est 100 fois supérieur.
Stratégie TTL dans KV :
// Le segment est en cache 24 heures
await env.USER_SEGMENTS.put(userId, JSON.stringify(segment), {
expirationTtl: 86400,
});
// La config est en cache 1 heure (peut changer fréquemment)
await env.SEGMENT_CONFIG.put(tier, JSON.stringify(config), {
expirationTtl: 3600,
});
Invalidation : lorsqu'un utilisateur se met à niveau, vous pouvez envoyer un signal via WebSocket ou webhook au worker pour mettre à jour KV. Cependant, ce n'est pas en temps réel — il faut accepter une eventual consistency (délai de 1–5 minutes).
Compromis de rendu : Static vs. Edge SSR
Edge SSR n'est pas toujours la meilleure solution. Comparaison :
| Métrique | Static (ISR) | Edge SSR | SSR d'origine |
|---|---|---|---|
| TTFB | 20ms | 40ms | 250ms |
| Personnalisation | Non | Oui | Oui |
| Ratio de cache hit | %99 | %60 | %10 |
| Coût (1M req) | $0.20 | $2.50 | $15 |
| Complexité | Faible | Moyenne | Élevée |
ISR (Incremental Static Regeneration) atteint un ratio de cache hit de %99, mais pas de personnalisation. Avec Edge SSR, le cache se fragmente par segment utilisateur — chaque segment crée une clé de cache différente, d'où un ratio de hit plus faible.
Approche hybride : le layout principal est statique, les composants personnalisés sont rendus à l'edge puis injectés côté client. Par exemple, la grille de produits est statique, mais "Recommandations pour vous" vient d'Edge SSR :
// Hybride : HTML statique + section personnalisée injectée depuis edge
const staticHTML = await env.STATIC_PAGES.get(pathname);
const personalizedSection = await renderPersonalizedRecommendations(userId);
const finalHTML = staticHTML.replace('<!--INJECT-->', personalizedSection);
Cette approche maintient le TTFB autour de 30ms tout en offrant de la personnalisation.
Débogage et monitoring : limites du runtime edge
Sur la production, déboguer un runtime edge est compliqué — les logs sont dispersés, les traces de pile d'erreurs incomplètes. Avec Cloudflare Workers, vous pouvez créer un flux de logs en temps réel via Tail Workers :
// tail-worker.js
export default {
async tail(events) {
for (const event of events) {
console.log(JSON.stringify({
timestamp: event.timestamp,
outcome: event.outcome,
logs: event.logs,
}));
}
},
};
Chez Vercel, console.log s'écoule dans les edge logs, diffusé via le tableau de bord Vercel. Cependant, en production, le logging verbeux peut dépasser la limite CPU — ne loggez que les événements critiques.
Métriques de monitoring :
- Latence de cold start : 80–120ms au premier chargement du worker — les requêtes warm prennent 15ms. Les routes fréquemment utilisées restent warm.
- Taux d'échec KV : %0.01 (SLA Cloudflare). Solution de secours : si KV ne se lit pas, utiliser un segment par défaut.
- Temps CPU : dépasser 50ms retourne une erreur %429. Profiling : mesurez avec
console.time(), déplacez les opérations lourdes vers l'origine.
Exemple de gestion d'erreurs :
try {
const segment = await env.USER_SEGMENTS.get(userId);
} catch (err) {
// Échec KV — utiliser la config par défaut
return renderHTML({ tier: 'free' }, pathname);
}
Si vous acceptez ces compromis avec Edge SSR, la réduction de 250ms à 40ms génère une différence mesurable en conversion. Particulièrement sur mobile, où la latence réseau est élevée, la proximité edge est critique. L'étape suivante : configurer correctement la KV store, définir votre stratégie de segments et tester les limites du runtime edge.