En 2026, la personalización SSR sigue siendo cara: transportar contexto del usuario al servidor de origen, consultar la base de datos, renderizar, devolver desde CDN. Latencia promedio: 200-300ms. Edge SSR elimina este ciclo — obtén datos del KV store en el punto más cercano al usuario, renderiza, devuelve. ¿Cuál es la arquitectura detrás de la latencia de 40ms en producción?
La Economía del Edge SSR
Con SSR basado en origen, cada request sigue la misma ruta: CDN edge → servidor origen → base de datos → lógica de aplicación → respuesta. El usuario está a 50ms, pero el origen está en Estambul y la base de datos en Fráncfort: round-trip mínimo 180ms. Edge SSR invierte esta economía: Cloudflare Workers o Vercel Edge Functions ejecutan código en el PoP (Punto de Presencia) a 15-30ms del usuario. Cuando el Key-Value store también está en la misma ubicación edge, la latencia total cae a 40-60ms.
El beneficio no es solo velocidad — también costo. Reservas el servidor de origen solo para mutaciones (POST/PUT/DELETE), el 90% del tráfico GET se cierra en edge. Cold start en Vercel Edge: 0-5ms. En Cloudflare Workers: ~1ms. En SSR tradicional, el cold start del contenedor Node.js oscila entre 500-1200ms. Esta diferencia afecta directamente la primera interacción.
En un sitio de e-commerce, personaliza fijación de precios específica del usuario, estado de inventario, contenido del carrito en edge. Cache el esqueleto HTML principal estáticamente, llena dinámicamente solo los bloques personalizados — "progressive enhancement". Cuando este enfoque híbrido alcanza 85% de cache hit rate, TTFB (Time to First Byte) cae a 30ms.
Arquitectura Cloudflare Workers + KV Store
Cloudflare Workers ejecutan en V8 isolates, no contenedores tradicionales — cada request corre en sandbox aislado, sin estado compartido. KV store es almacenamiento clave-valor eventualmente consistente, replicado globalmente. Objetivos de latencia: lectura 10-30ms, escritura 100-200ms (por replicación asíncrona). Setup:
// worker.js — Punto de entrada Edge SSR
export default {
async fetch(request, env) {
const url = new URL(request.url);
const userId = getUserId(request); // Extrae de cookie
// Obtén contexto del usuario desde KV
const userCtx = await env.USER_KV.get(`user:${userId}`, { type: 'json' });
if (!userCtx) {
return new Response('Unauthorized', { status: 401 });
}
// Renderiza HTML personalizado
const html = renderPersonalizedPage({
userName: userCtx.name,
cart: userCtx.cart,
recentlyViewed: userCtx.recentlyViewed,
});
return new Response(html, {
headers: {
'Content-Type': 'text/html;charset=UTF-8',
'Cache-Control': 'private, max-age=0',
},
});
},
};
function renderPersonalizedPage(data) {
// Lógica template simple — en producción usa Vue/React
return `
<!DOCTYPE html>
<html>
<head><title>Bienvenido ${data.userName}</title></head>
<body>
<h1>Hola ${data.userName}</h1>
<p>Tienes ${data.cart.length} artículos en el carrito</p>
<ul>
${data.recentlyViewed.map(p => `<li>${p}</li>`).join('')}
</ul>
</body>
</html>
`;
}
Estructura de datos KV:
- Clave:
user:{userId} - Valor: JSON —
{ name, cart, recentlyViewed, priceTier } - TTL: 3600s (cache 1 hora, luego refresh desde origen)
Con este setup, cada lectura cuesta 15-25ms — sin ir a Postgres en Fráncfort, sin network hops adicionales. El path de escritura es diferente: cuando llega una mutación, POST al origen API, el origen actualiza base de datos y escribe a KV de forma asíncrona. Como KV es "eventual", después de 100ms todos los nodos edge tienen los datos nuevos.
Alternativa: Vercel Edge Functions
Vercel Edge se integra nativamente con Next.js — funciona basado en middleware. Setup:
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export async function middleware(req: NextRequest) {
const userId = req.cookies.get('userId')?.value;
if (!userId) {
return NextResponse.redirect(new URL('/login', req.url));
}
// Vercel KV (compatible con Redis, infraestructura Upstash)
const userCtx = await fetch(`https://YOUR_KV_ENDPOINT/get/user:${userId}`);
const data = await userCtx.json();
// Añade contexto a headers de request, pasa al siguiente handler
const response = NextResponse.next();
response.headers.set('X-User-Context', JSON.stringify(data));
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/checkout/:path*'],
};
Cold start en Vercel Edge: 3-8ms, un poco más lento que Cloudflare, pero la integración con ISR (Incremental Static Regeneration) de Next.js es sólida. Genera una página estáticamente, enriquécela con contexto del usuario en edge — "streaming SSR". Ejemplo: layout principal static HTML, widget de usuario inyectado en edge.
Trade-offs: Tamaño de Bundle, Debugging, Costo
El runtime edge es limitado — no tienes la API completa de Node.js. En Cloudflare Workers, los módulos nativos de Node no funcionan (fs, child_process), lo mismo en Vercel Edge. Necesitas minimizar dependencias. Ejemplo: date-fns (70KB) → dayjs (2KB), lodash → métodos nativos de ES6.
Límites de tamaño de bundle:
- Cloudflare Workers: 1MB (límite duro), script comprimido máx 5MB
- Vercel Edge: 1MB (middleware)
En producción no debes superar 200KB — cada KB agrega 0.5-1ms a latencia (parse + execute). Tree-shaking y code splitting son críticos. Si usas React, preact (3KB) es más sensato.
Debugging: console.log existe en edge pero stack traces son limitados. Con Cloudflare Wrangler CLI (wrangler dev) creas un entorno de test local, en Vercel vercel dev simula el runtime edge. En producción, necesitas servicio de error tracking tipo Sentry — envías logs de error vía HTTP POST desde el isolate edge.
Costo: Cloudflare Workers: primeros 100K requests/día gratis, luego $0.50/millón. KV storage: 1GB gratis, después $0.50/10 millones de lecturas. Vercel Edge Functions: depende del plan — Pro incluye 1 millón de ejecuciones. Para 10 millones requests/mes, costo edge $20-40/mes vs $150-200/mes en servidor tradicional. A mayor escala, ventaja edge aumenta.
Estrategia KV Store: Write-Through vs Write-Behind
Cómo escribes a KV afecta latencia directamente. Dos patrones:
Write-Through (Síncrono): Cuando origin API recibe mutación, escribe a DB y KV, espera ambos, devuelve response. Garantiza consistencia pero write latency 150-250ms (dos network hops).
// Handler de origin API
app.post('/cart/add', async (req, res) => {
const { userId, productId } = req.body;
// 1. Escribe a Postgres
await db.query('INSERT INTO cart_items ...');
// 2. Actualiza KV
const userCtx = await getUserContext(userId);
userCtx.cart.push(productId);
await kv.put(`user:${userId}`, JSON.stringify(userCtx));
res.json({ success: true });
});
Write-Behind (Asíncrono): Escribe a DB, devuelve response, background job actualiza KV después. Write latency 50-80ms pero riesgo de staleness en KV (100-200ms).
app.post('/cart/add', async (req, res) => {
const { userId, productId } = req.body;
await db.query('INSERT INTO cart_items ...');
// Delega actualización KV a job asíncrono
queueKVUpdate('user', userId);
res.json({ success: true });
});
async function queueKVUpdate(type, id) {
// Redis queue o Cloudflare Durable Objects
await redis.lpush('kv_updates', JSON.stringify({ type, id }));
}
Para e-commerce, write-behind para agregar carrito es sensato — usuario no nota 100ms de delay, en checkout se double-check con origin. Para datos críticos como cambios de precio, write-through es mejor.
Capas de Cache Híbridas: Estático + Edge SSR
Edge SSR puro no es óptimo — combina estático + dinámico híbrido. Ejemplo: En proyectos de Headless Commerce de Roibase, generamos esqueleto de página principal (header, footer, categorías generales) como static, inyectamos bloques específicos del usuario (icono carrito, nombre, widget recomendaciones) en edge. Resultado: cache hit rate sube a 92%.
Estructura en Next.js:
// app/page.tsx — Layout estático
export default function HomePage() {
return (
<main>
<Header /> {/* Estático */}
<HeroSection /> {/* Estático */}
<UserWidget /> {/* Edge SSR */}
<ProductGrid /> {/* Static ISR, revalidar cada 60s */}
</main>
);
}
// components/UserWidget.tsx — Server component, runtime edge
export const runtime = 'edge';
export default async function UserWidget() {
const userId = cookies().get('userId')?.value;
const userCtx = await fetch(`https://kv.../user:${userId}`);
const data = await userCtx.json();
return <div>Bienvenido {data.name}</div>;
}
Con este setup, 80% del HTML viene de CDN estático (TTFB 8-12ms), 20% se renderiza en edge (adicional 30-40ms). TTFB total 40-50ms. La misma página en full SSR basado en origin tardaba 180-220ms.
Mejora con Streaming SSR: React 18 con Suspense: devuelve la parte estática inmediatamente, streamea la parte SSR del edge. Navegador comienza parse en 20ms, widget personalizado llega con "hydration" en 30ms más. Latencia percibida cae a 20ms.
Caso de Producción: Cómo se Logró 40ms
Caso real: sitio e-commerce basado en Shopify Hydrogen, Cloudflare Workers + KV. Latencia inicial 210ms (origen en Fráncfort, usuario en Estambul), objetivo <50ms.
Optimizaciones aplicadas:
- Compresión estructura de datos KV: Redujimos JSON contexto de usuario de 2.4KB a 800 bytes — solo campos críticos (userId, cart, priceTier). Movimos "recently viewed" a clave separada (
user:{id}:recent). - Reducción de bundle: Preact en lugar de React (3KB vs 40KB),
Intl.DateTimeFormatnativo en lugar de date-fns. Worker bundle bajó de 180KB a 65KB. - Cache híbrido: Página principal estática (CDN 300s), solo botón "Add to Cart" y precio con edge SSR. Cache hit rate 88% → 94%.
- Routing de edge PoP: Activamos "Smart Routing" de Cloudflare — dirige usuarios al PoP con menor latencia. Usuario de Estambul va a PoP de Sofía (22ms RTT) en lugar de Fráncfort.
Resultados: TTFB 210ms → 42ms (mediana), LCP 2.1s → 0.9s, INP 180ms → 95ms. Conversion rate creció 2.3% → 2.9% (+26% lift). Costo mensual de servidor origen: $340 → $95 (costo edge: $28/mes).
El auge del Edge SSR acelera en 2026 — Cloudflare, Vercel, Fastly todos prometen latencia sub-50ms. Con arquitectura KV store correcta, personalización se resuelve sin ir a origen. Trade-offs existen: límites de bundle size, debugging más difícil, riesgo de eventual consistency. Pero en escenarios correctos (e-commerce, dashboard, SaaS) la ganancia es sólida. 40ms ya no es lujo, es estándar.