SEO y Tarjetas de WhatsApp en SPAs sin dolor de cabeza: La técnica «Edge Bot-Proxy» en Cloudflare Pages

Tiempo de lectura: 4 minutos
Cómo logramos previsualizaciones enriquecidas en WhatsApp, Twitter y posicionamiento SEO en una Single Page Application (Angular) sin desplegar un servidor SSR completo ni perder el rendimiento de una PWA.

1. El eterno dilema de las Single Page Applications (SPA)

Las Single Page Applications (Angular, React, Vue o Svelte) ofrecen una experiencia de usuario inmejorable: transiciones fluidas, funcionamiento sin conexión como PWA, acceso a APIs nativas (cámara para escanear códigos de barras, geolocalización de farmacias, síntesis de voz, etc.) y un despliegue estático barato y escalable.

Sin embargo, tienen un talón de Aquiles histórico: el compartir en redes sociales y el SEO.

Cuando un usuario comparte el enlace de un medicamento concreto en WhatsApp o Telegram:

https://prospectofacil.com/medicamento/57620

El rastreador de WhatsApp no ejecuta JavaScript. Hace una petición GET básica y lee el index.html estático original:

<!doctype html>
<html>
  <head>
    <title>Prospecto Fácil</title>
    <meta property="og:title" content="Prospecto Fácil">
    <meta property="og:description" content="Información de medicamentos...">
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>

El resultado es frustrante: todas las tarjetas de previsualización en WhatsApp muestran la descripción genérica de la home, en lugar del nombre del medicamento, su principio activo y la foto de su envase oficial.


2. La solución tradicional y sus costes: SSR Completo

La respuesta habitual de la industria es implementar Server-Side Rendering (SSR) completo (como Angular Universal / @angular/ssr, Next.js o Nuxt):

  • Problema 1: Conflictos de APIs de navegador: Librerías que interactúan con la cámara (@zxing/library), mapas interactivos (leaflet) o localStorage fallan en el servidor si no están minuciosamente blindadas.
  • Problema 2: Complejidad de infraestructura: Ya no puedes alojar simples archivos estáticos en un CDN global gratuito. Necesitas contenedores Docker, Cloud Run o Node.js siempre activos, lo que incrementa la latencia y los costes de infraestructura.
  • Problema 3: Hidratación y sobrecarga: El servidor gasta ciclos de CPU renderizando árboles de componentes pesados para peticiones que sólo necesitaban tres etiquetas <meta>.

3. La tercera vía: El «Edge Bot-Proxy» con Cloudflare Pages

¿Y si los usuarios humanos siguen recibiendo la SPA estática ultrarrápida desde el CDN, y únicamente los bots y redes sociales reciben el HTML con metadatos dinámicos inyectados al vuelo en el Edge?

Esta arquitectura híbrida aprovecha dos capacidades clave de Cloudflare Pages:

  1. Cloudflare Pages Functions: Código serverless que corre en el Edge sobre el runtime V8 de Cloudflare Workers con latencia de un solo dígito de milisegundos.
  2. HTMLRewriter: Una API nativa de streaming en C++ que puede parsear y reescribir fragmentos de HTML al vuelo sin tener que cargar todo el DOM en memoria.

4. Implementación paso a paso (Caso Real: Prospecto Fácil)

Paso 1: Enrutamiento en el Edge

En Cloudflare Pages, basta con crear una carpeta functions/ en la raíz del repositorio. Para interceptar cualquier medicamento, creamos: functions/medicamento/[id].ts

Paso 2: Detección precisa de Crawlers

Identificamos a los agentes que requieren metadatos estáticos inmediatos:

const BOT_REGEX =
  /googlebot|bingbot|duckduckbot|facebot|facebookexternalhit|twitterbot|linkedinbot|slackbot|telegrambot|whatsapp|applebot|gptbot|claude-web|perplexitybot/i;

export const onRequest = async (context) => {
  const { request, params } = context;
  const userAgent = request.headers.get('user-agent') || '';
  const isBot = BOT_REGEX.test(userAgent);

  // Obtenemos la respuesta estática base (el index.html de la SPA)
  const response = await context.next();
  const medId = params.id;

Paso 3: Consulta de datos al vuelo con tolerancia a fallos

En el Edge consultamos la API oficial de CIMA (o nuestro catálogo en memoria para medicamentos frecuentes), aplicando siempre un timeout estricto para no penalizar el tiempo de respuesta:

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 2000);

const cimaRes = await fetch(`https://cima.aemps.es/cima/rest/medicamento?nregistro=${encodeURIComponent(medId)}`, {
  signal: controller.signal,
  headers: { Accept: 'application/json' },
});
clearTimeout(timeoutId);

const med = await cimaRes.json();

Paso 4: Inyección en streaming con HTMLRewriter

Aquí es donde ocurre la magia. Sin parsear HTML con librerías pesadas como Cheerio o JSDOM, HTMLRewriter sustituye las etiquetas <meta> en streaming:

const title = `${med.nombre} — Prospecto y Posología Oficial | Prospecto Fácil`;
const desc = `Prospecto oficial y posología de ${med.nombre} (${med.pactivos}). Información clara y accesible.`;
const image = med.fotos?.[0]?.url || 'https://prospectofacil.com/icon-512.png';

let rewriter = new HTMLRewriter()
  .on('title', { element(e) { e.setInnerContent(title); } })
  .on('meta[property="og:title"]', { element(e) { e.setAttribute('content', title); } })
  .on('meta[property="og:description"]', { element(e) { e.setAttribute('content', desc); } })
  .on('meta[property="og:image"]', { element(e) { e.setAttribute('content', image); } })
  .on('meta[name="twitter:card"]', { element(e) { e.setAttribute('content', 'summary_large_image'); } })
  .on('head', {
    element(e) {
      // Inyección de microdatos Schema.org / Drug para SEO médico
      e.append(`<script type="application/ld+json">${JSON.stringify(drugSchema)}</script>`, { html: true });
    }
  });

// Si es un bot o motor de IA, inyectamos un resumen semántico dentro de <app-root>
if (isBot) {
  rewriter = rewriter.on('app-root', {
    element(e) {
      e.setInnerContent(semanticCrawlerHtml, { html: true });
    },
  });
}

return rewriter.transform(response);

5. Resultados y Ventajas Clave

AspectoSPA ClásicaSSR Tradicional (Node.js)Edge Bot-Proxy (Cloudflare)
Tarjetas en WhatsApp / Redes❌ Genéricas / Vacías✅ Dinámicas✅ Dinámicas con foto oficial
Indexación en Google / Bots IA⚠️ Depende de ejecución JS✅ Completa✅ Completa con Schema.org y HTML semántico
Coste de Servidores$0 (Estático)$$ (VPS / Cloud Run)$0 (Capa gratuita Cloudflare)
Complejidad de Código
Mínima
Alta (Hidratación, Server Mocks)Aislada en 1 función Edge
Velocidad para usuarios realesInstantánea (CDN)Depende del render del servidorInstantánea (Puro CDN Edge)

6. Conclusión

No siempre necesitas rehacer tu aplicación para añadir un framework SSR complejo solo para tener buen SEO y compartir enlaces con tarjetas visuales atractivas.

Con el patrón Edge Bot-Proxy sobre Cloudflare Pages:

  • Mantienes la simplicidad y agilidad de tu SPA.
  • Aprovechas la red de baja latencia de Cloudflare en todo el mundo.
  • Tus usuarios disfrutan de una experiencia PWA nativa mientras los rastreadores de WhatsApp, Twitter y Google reciben exactamente el HTML y los metadatos que esperan.

Deja un comentario

Este sitio está protegido por reCAPTCHA y se aplican la política de privacidad y los términos de servicio de Google.