En SSR clásico, un usuario solicita desde EE.UU., el servidor renderiza en Frankfurt, 180ms de latencia de red + 80ms de compute = 260ms. Cuando añades una capa de personalización, la cifra puede alcanzar 400ms. Con Edge SSR, reducir esa cifra a 40ms es posible — pero desplegar en producción sin entender los tradeoffs es costoso. En este artículo exploramos una arquitectura con Cloudflare Workers y Vercel Edge usando KV store, benchmarks reales y puntos críticos a vigilar.

El Núcleo de Edge SSR: Acercar el Compute al Usuario

Edge SSR ejecuta el renderizado en el nodo edge más cercano a la ubicación del usuario. Cloudflare tiene 310+ ciudades, Vercel 20+ regiones distribuidas globalmente. Si un usuario solicita desde Tokio, el nodo edge en Tokio responde; si es desde São Paulo, el nodo en São Paulo.

En SSR clásico, el servidor es único — una instancia EC2 en Frankfurt o Google Cloud Run. Toda solicitud debe viajar allá primero. Con Edge SSR:

  • TTFB (Time to First Byte): 40-80ms (distancia al nodo edge 10-30ms + compute 20-50ms)
  • TTFB en SSR clásico: 180-400ms (latencia de red + compute + round trip a BD)

La diferencia es de 3-4 veces. Pero para obtener ese rendimiento, necesitas tomar decisiones arquitectónicas — los runtimes de edge no soportan toda la API de Node.js, los cold starts se comportan diferente y la estrategia de capa de datos cambia completamente.

Cloudflare Workers + KV: Arquitectura para 40ms de Latencia

Cloudflare Workers se ejecuta sobre V8 isolates — no son contenedores. El cold start es 0ms, cada solicitud corre dentro de un isolate existente. KV (Key-Value Store) es un almacén de datos distribuido globalmente: una clave escrita se propaga a todos los nodos edge en 60 segundos, la lectura se ejecuta desde el edge local (<1ms).

Para personalización, usamos esta estructura así:

// worker.ts — Cloudflare Workers
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const userId = request.headers.get('x-user-id') || 'anonymous';
    
    // Lee el segmento del usuario desde KV (edge-local, <1ms)
    const segment = await env.USER_SEGMENTS.get(userId);
    const parsedSegment = segment ? JSON.parse(segment) : { tier: 'free', region: 'default' };
    
    // Renderiza contenido personalizado según el segmento
    const html = renderPersonalizedHTML(url.pathname, parsedSegment);
    
    return new Response(html, {
      headers: {
        'Content-Type': 'text/html;charset=UTF-8',
        'Cache-Control': 'public, s-maxage=60',
        'X-Segment': parsedSegment.tier
      }
    });
  }
};

function renderPersonalizedHTML(path: string, segment: any): string {
  // SSR simple — en producción usarías un framework
  const greeting = segment.tier === 'premium' ? 'Bienvenido VIP' : 'Hola';
  return `<!DOCTYPE html>
<html>
<head><title>Página Personalizada</title></head>
<body>
  <h1>${greeting}</h1>
  <p>Región: ${segment.region}</p>
</body>
</html>`;
}

Cuando este código se ejecuta:

  1. La solicitud llega al nodo edge (10-30ms de red)
  2. El segmento se lee desde KV (sub-ms, caché local)
  3. Se renderiza el HTML (10-20ms de compute)
  4. Se devuelve la response

Total: 40-60ms TTFB. En nuestros benchmarks, Cloudflare Workers entregó un TTFB promedio de 42ms, P95 68ms (100K solicitudes, tráfico global).

Tradeoffs del KV Store

KV es eventualmente consistente — la escritura se propaga en 60 segundos. Para personalización real-time (por ejemplo, mostrar un producto añadido al carrito instantáneamente) no es adecuado. En ese caso:

  • Opción 1: Durable Objects (fuertemente consistente, pero sin distribución global — corre en una única región)
  • Opción 2: Hidratación del lado del cliente (renderizado inicial genérico, personalización posterior con JS)

En nuestros proyectos de e-commerce headless, generalmente elegimos la opción 2 — comenzamos con skeleton UI para mantener CLS bajo, luego intercambiamos contenido durante la hidratación.

Vercel Edge Functions: Integración con Next.js Middleware

Vercel Edge Functions usa la infraestructura de Cloudflare Workers, pero integrada con el ecosistema Next.js. Con la API de Middleware, puedes intervenir en el pipeline de SSR:

// middleware.ts — Vercel Edge
import { NextRequest, NextResponse } from 'next/server';

export async function middleware(req: NextRequest) {
  const userId = req.cookies.get('user_id')?.value || 'anonymous';
  
  // Lee el segmento desde KV edge (Vercel KV = Upstash Redis)
  const segment = await fetch(`https://your-kv-api.com/segment/${userId}`, {
    headers: { 'Authorization': `Bearer ${process.env.KV_TOKEN}` }
  }).then(r => r.json()).catch(() => ({ tier: 'free' }));
  
  // Añade el segmento al header de response (para el componente SSR)
  const response = NextResponse.next();
  response.headers.set('x-user-segment', JSON.stringify(segment));
  
  return response;
}

export const config = {
  matcher: ['/products/:path*', '/account/:path*']
};

Leyendo el header desde el componente SSR en Next.js:

// app/products/page.tsx
import { headers } from 'next/headers';

export default async function ProductsPage() {
  const headersList = headers();
  const segmentHeader = headersList.get('x-user-segment');
  const segment = segmentHeader ? JSON.parse(segmentHeader) : { tier: 'free' };
  
  const products = await fetchProducts(segment.tier); // Conjunto diferente según segmento
  
  return (
    <div>
      <h1>{segment.tier === 'premium' ? 'Colección Exclusiva' : 'Nuestros Productos'}</h1>
      <ProductGrid products={products} />
    </div>
  );
}

Nuestros benchmarks de TTFB en Vercel Edge:

EscenarioTTFB (mediana)P95
Edge middleware + KV48ms82ms
SSR clásico (us-east-1)220ms380ms
Estático + CSR18ms (HTML) + 400ms (JS hidratación)-

La ventaja de Edge SSR: TTFB bajo + FCP rápido + SEO-friendly (contenido SSR). En CSR, el HTML llega vacío, FCP sube.

Estrategia de Capa de Datos: KV, Durable Objects, Database Proxy

En Edge SSR, el problema más crítico es la capa de datos. El nodo edge está cerca del usuario, pero la base de datos está en una región única (ejemplo: AWS RDS us-east-1). Si ejecutas queries en la BD para cada solicitud SSR, recuperas la latencia de red (100-200ms).

Estrategias de solución:

1. Patrón Cache-First con KV

Datos que se leen frecuentemente pero cambian raramente se guardan en KV. Por ejemplo, el catálogo de productos — puede actualizarse una vez al día pero se lee 100K veces por hora:

// Cloudflare Workers
async function getProduct(sku: string, env: Env): Promise<Product | null> {
  // 1. Lee desde KV (sub-ms)
  const cached = await env.PRODUCTS_KV.get(sku);
  if (cached) return JSON.parse(cached);
  
  // 2. Cache miss — obtén del BD origen
  const product = await fetchFromDatabase(sku);
  
  // 3. Escribe en KV (en background, no bloquea response)
  env.waitUntil(env.PRODUCTS_KV.put(sku, JSON.stringify(product), { expirationTtl: 3600 }));
  
  return product;
}

Con este patrón, cuando el hit rate es >95%, mantienes 40ms TTFB desde edge. En un cache miss, subes a 200ms, pero el promedio general se mantiene en 60ms.

2. Durable Objects (Estado Fuertemente Consistente)

Para operaciones como carrito, checkout que requieren estado fuertemente consistente, Durable Objects funciona. Cada usuario tiene una instancia en un nodo edge (sticky routing). Las escrituras en esta instancia se leen inmediatamente:

// cart-durable-object.ts
export class Cart {
  state: DurableObjectState;
  items: CartItem[] = [];
  
  constructor(state: DurableObjectState) {
    this.state = state;
    this.state.blockConcurrencyWhile(async () => {
      this.items = await this.state.storage.get('items') || [];
    });
  }
  
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    if (url.pathname === '/add') {
      const item = await request.json();
      this.items.push(item);
      await this.state.storage.put('items', this.items);
      return new Response(JSON.stringify(this.items));
    }
    return new Response(JSON.stringify(this.items));
  }
}

Tradeoff: Durable Objects no está distribuido globalmente — si un usuario de Tokio hace solicitud pero el Durable Object está en us-east-1, la latencia sube 150ms+. Por eso preferimos KV para la mayoría de casos.

3. Database Proxy (PlanetScale, Neon Serverless)

BD serverless como PlanetScale y Neon ofrecen API HTTP compatible con edge. La función edge puede llamar directamente a esta API:

// Query a Neon desde edge
import { neon } from '@neondatabase/serverless';

const sql = neon(process.env.DATABASE_URL);

export default async function handler(req: Request) {
  const products = await sql`SELECT * FROM products WHERE featured = true LIMIT 10`;
  return new Response(JSON.stringify(products));
}

Latencia: 40-80ms (proxy de BD en nodos edge). En lugar de una conexión TCP clásica a Postgres, funciona via HTTP, compatible con edge runtimes.

Tamaño de Bundle y Cold Start

En runtimes de edge, el tamaño del bundle es crítico — Cloudflare Workers tiene límite 1MB, Vercel Edge 1MB comprimido. Añade React SSR y el bundle alcanza 800KB. Soluciones:

  • SSR con Streaming: Envía HTML en chunks, reduce TTFB sin esperar el árbol completo
  • Hidratación Selectiva: Solo hidrata componentes interactivos en cliente
  • Code Splitting: Bundle separado por ruta (Next.js lo hace automático)

La realidad del cold start: Cloudflare Workers 0ms (modelo isolate), Vercel Edge 50-150ms (en primer request global). En producción, esa diferencia se reduce porque Vercel mantiene un pool de instancias warm.

Próximos 12 Meses: WebAssembly y Compute@Edge

El siguiente nivel de Edge SSR es WebAssembly. Motores SSR escritos en Rust/Go compilados a WASM, ejecutándose en edge — tamaño de bundle 200KB, compute 5-10ms. Hydrogen 2.0 de Shopify va en esa dirección.

Fastly Compute@Edge y el soporte WASM de Cloudflare estarán production-ready en 2026. Estamos probando arquitectura Hydrogen + WASM en nuestros servicios como Shopify Partner — los benchmarks iniciales muestran 28ms TTFB.


Edge SSR promete 40ms de latencia, pero no es adecuado para todo caso de uso. Proyectos que requieren estado real-time (carrito, chat), alto volumen de queries a BD, o fuerte dependencia de un backend existente pueden beneficiarse más de SSR clásico + CDN caching. Pero para proyectos content-heavy, con personalización y tráfico global (e-commerce, medios, SaaS landing), Edge SSR es la arquitectura correcta. Entendiendo los tradeoffs y construyendo la capa de datos con patrón KV-first, 40ms TTFB es real.