¿Alguna vez has hecho clic en un enlace y la página tardó ese par de segundos que se sienten eternos? Bueno, la Speculation Rules API existe precisamente para eliminar esa espera. Es una API del navegador que le dice al browser qué páginas debería ir descargando o renderizando en segundo plano, para que cuando el usuario finalmente haga clic, la transición sea prácticamente instantánea.
Y lo mejor: no es tan complicada de implementar como suena.
En esta guía vas a aprender cómo funciona, las diferencias entre prefetch y prerender, cómo configurarla paso a paso con código real, y las mejores prácticas para que no te exploten las analíticas ni te metas en problemas con efectos secundarios. Vamos a ello.
¿Qué es la Speculation Rules API?
La Speculation Rules API es una funcionalidad relativamente reciente que te permite definir reglas en formato JSON para que el navegador cargue o renderice páginas de forma especulativa, antes de que el usuario haga clic. Es, básicamente, el reemplazo moderno del viejo <link rel="prerender"> (que ya nadie debería estar usando) y ofrece bastante más control y seguridad que los métodos de prefetch anteriores.
Las reglas se definen dentro de un bloque <script type="speculationrules"> en tu HTML o en un archivo JSON externo que referencias con el encabezado HTTP Speculation-Rules. El navegador lee estas reglas y decide cuándo ejecutarlas según las condiciones del dispositivo, la conexión y lo que esté haciendo el usuario.
Prefetch vs. Prerender: ¿Cuál elegir?
La API ofrece dos tipos de acciones especulativas. Entender la diferencia entre ellas es clave, porque elegir mal puede costarte recursos sin dar beneficio real.
Prefetch
Descarga los recursos principales de la página destino (HTML, CSS, JavaScript) pero no la renderiza. Cuando el usuario navega a esa página, el navegador todavía necesita procesar y pintar el contenido, pero al menos la descarga ya está hecha. Es la opción ligera: menos memoria, menos ancho de banda, menos CPU.
Prerender
Aquí la cosa se pone seria. Descarga y renderiza completamente la página en una pestaña invisible almacenada en memoria. Todo: sub-recursos, JavaScript, peticiones de datos, todo. Cuando el usuario navega, el navegador simplemente activa esa pestaña invisible en lugar de hacer el proceso de navegación habitual. El resultado es una transición que se siente instantánea de verdad.
Pero claro, ese poder tiene un coste.
| Característica | Prefetch | Prerender |
|---|---|---|
| Descarga recursos | Sí | Sí |
| Ejecuta JavaScript | No | Sí |
| Renderiza la página | No | Sí |
| Consumo de memoria | Bajo | Alto |
| Velocidad al navegar | Rápida | Instantánea |
| Riesgo de efectos secundarios | Bajo | Alto |
Regla general: usa prefetch cuando la probabilidad de navegación es moderada, y reserva el prerender para cuando estés bastante seguro de que el usuario va a visitar esa página (por ejemplo, el siguiente paso en un flujo de compra o checkout).
Implementación paso a paso
1. Reglas por lista de URLs (List Rules)
La forma más directa. Simplemente especificas las URLs que quieres precargar:
<script type="speculationrules">
{
"prerender": [
{
"urls": ["/productos", "/contacto", "/ofertas"]
}
]
}
</script>
Esto le dice al navegador que prerenderice esas tres páginas. Como las list rules usan eagerness: "immediate" por defecto, el prerender arranca lo antes posible tras cargar la página actual. Sencillo y efectivo.
2. Reglas por documento (Document Rules)
Aquí es donde la cosa se pone interesante. Las document rules son más potentes y escalables: en lugar de listar URLs a mano, defines condiciones que el navegador aplica a todos los enlaces del documento:
<script type="speculationrules">
{
"prefetch": [
{
"where": {
"and": [
{ "href_matches": "/*" },
{ "not": { "href_matches": "/logout" } },
{ "not": { "href_matches": "/api/*" } },
{ "not": { "selector_matches": ".no-prefetch" } }
]
},
"eagerness": "moderate"
}
]
}
</script>
Este ejemplo hace prefetch de todos los enlaces internos excepto los de logout, las rutas de API y cualquier elemento con la clase .no-prefetch. El eagerness: "moderate" hace que el prefetch se active cuando el usuario pasa el cursor sobre un enlace durante unos 200 milisegundos. Nada mal, ¿verdad?
3. Reglas vía encabezado HTTP
Si no puedes (o no quieres) tocar el HTML, puedes definir las reglas desde el servidor usando el encabezado HTTP Speculation-Rules:
# Nginx
location / {
add_header Speculation-Rules "/speculation-rules.json";
}
# Apache
Header set Speculation-Rules "/speculation-rules.json"
Y el archivo speculation-rules.json contiene las mismas reglas en formato JSON:
{
"prefetch": [
{
"where": { "href_matches": "/*" },
"eagerness": "moderate"
}
]
}
Este enfoque centraliza la configuración y viene genial para sitios grandes donde prefieres gestionar todo desde el backend sin andar tocando plantillas del frontend.
Controlar la urgencia con eagerness
La propiedad eagerness es, sinceramente, una de las partes más importantes de toda la API. Controla cuán agresivamente el navegador ejecuta la especulación, y es tu herramienta para equilibrar rendimiento y consumo de recursos:
| Valor | Cuándo se activa | Caso de uso ideal |
|---|---|---|
immediate | Lo antes posible al cargar la página | Páginas con navegación predecible (siguiente paso de un formulario) |
eager | Ante cualquier señal leve de intención | Similar a immediate; en móvil, desde enero 2026, se activa 50ms después de que el enlace entra en el viewport |
moderate | Al pasar el cursor 200ms o al presionar (pointerdown) | Navegación general: menús, listados, artículos |
conservative | Al hacer touch/mouse down (justo antes del clic) | Cuando quieres mínimo consumo pero algo de ventaja |
Dato que mucha gente pasa por alto: las list rules usan immediate por defecto, mientras que las document rules usan conservative por defecto. Además, con moderate y conservative, el navegador opera con un sistema FIFO (primero en entrar, primero en salir) limitado a 2 especulaciones simultáneas para no comerse toda la memoria.
Estrategia combinada: prefetch + prerender progresivo
Una de las estrategias más efectivas que he visto funcionar en producción es combinar ambos métodos de forma progresiva. La idea es ir aumentando la agresividad de la especulación conforme crece la confianza en la intención del usuario:
<script type="speculationrules">
{
"prefetch": [
{
"where": {
"and": [
{ "href_matches": "/*" },
{ "not": { "href_matches": "/logout" } },
{ "not": { "href_matches": "/carrito/*" } },
{ "not": { "selector_matches": "[data-no-speculate]" } }
]
},
"eagerness": "moderate"
}
],
"prerender": [
{
"where": {
"and": [
{ "href_matches": "/*" },
{ "not": { "href_matches": "/logout" } },
{ "not": { "href_matches": "/api/*" } }
]
},
"eagerness": "conservative"
}
]
}
</script>
¿Qué consigues con esto? El navegador hace prefetch al pasar el cursor por un enlace (200ms de hover) y solo lanza un prerender completo cuando el usuario realmente presiona el enlace (mouse o touch down). Es lo mejor de ambos mundos: velocidad percibida máxima sin desperdiciar recursos en páginas que probablemente nadie visitará.
Gestionar analíticas y efectos secundarios
Este es el punto donde más gente se tropieza. Como el prerender ejecuta todo el JavaScript de la página, puede inflar las métricas de analíticas, disparar píxeles de conversión o causar efectos secundarios que no quieres. Si no lo manejas bien, tus datos de tráfico van a ser un desastre.
Así es como se soluciona:
Detectar si la página está siendo prerenderizada
// Comprobar si estamos en estado de prerender
if (document.prerendering) {
console.log("Esta página está siendo prerenderizada");
}
// Ejecutar código solo cuando la página se active realmente
function onActivation(callback) {
if (document.prerendering) {
document.addEventListener("prerenderingchange", callback, { once: true });
} else {
callback();
}
}
// Ejemplo: disparar analíticas solo al activarse
onActivation(() => {
gtag("event", "page_view", {
page_title: document.title,
page_location: window.location.href
});
});
Diferir scripts de terceros
Para scripts de terceros que no saben nada del prerender (y hay bastantes), lo mejor es no cargarlos hasta después de la activación:
onActivation(() => {
// Cargar scripts de analíticas/ads solo al activarse
const script = document.createElement("script");
script.src = "https://example.com/analytics.js";
document.head.appendChild(script);
});
Detección en el servidor
El servidor también puede detectar solicitudes especulativas gracias al encabezado Sec-Purpose:
# En Node.js/Express
app.get("/pagina", (req, res) => {
const isSpeculative = req.headers["sec-purpose"]?.includes("prefetch");
if (isSpeculative) {
// No registrar la visita en analíticas del servidor
// No ejecutar lógica de efectos secundarios
}
res.render("pagina");
});
Buenas noticias: Google Analytics 4, Google Publisher Tag y New Relic ya son conscientes del prerendering y no registran vistas de página hasta que la página se activa de verdad. Pero ojo, si usas soluciones de analíticas personalizadas o proveedores menos comunes, vas a tener que implementar las protecciones tú mismo.
Compatibilidad de navegadores y mejora progresiva
A día de hoy (marzo de 2026), la Speculation Rules API tiene soporte en navegadores basados en Chromium: Chrome, Edge y Opera. Eso viene a ser aproximadamente el 70% del mercado global de navegadores, lo cual no está nada mal.
Firefox ha pasado a tener una posición "positiva" respecto al prefetch y muestra interés en implementarla, aunque de momento no hay soporte. Safari tiene un issue de seguimiento abierto pero tampoco la soporta aún (nada sorprendente, siendo Safari).
Lo que sí es importante: implementar speculation rules es completamente seguro. Los navegadores que no la soportan simplemente ignoran el bloque <script type="speculationrules"> sin producir errores ni efectos adversos. Es una mejora progresiva de libro.
Si necesitas detectar el soporte por código:
if (HTMLScriptElement.supports &&
HTMLScriptElement.supports("speculationrules")) {
console.log("Speculation Rules API soportada");
// Insertar reglas dinámicamente si es necesario
const specScript = document.createElement("script");
specScript.type = "speculationrules";
specScript.textContent = JSON.stringify({
prefetch: [{ where: { href_matches: "/*" }, eagerness: "moderate" }]
});
document.head.appendChild(specScript);
}
Caso práctico: e-commerce con carga instantánea
Vamos con un ejemplo del mundo real. Imagina una tienda online donde los usuarios navegan entre listados de productos y páginas de detalle. Este es un escenario perfecto para la API:
<script type="speculationrules">
{
"prefetch": [
{
"where": {
"and": [
{ "selector_matches": ".product-card a" },
{ "not": { "selector_matches": ".out-of-stock a" } }
]
},
"eagerness": "moderate"
}
],
"prerender": [
{
"where": {
"selector_matches": ".product-card a"
},
"eagerness": "conservative"
},
{
"urls": ["/checkout"],
"eagerness": "moderate"
}
]
}
</script>
¿Qué hace exactamente esta configuración?
- Prefetch al hover: cuando el usuario pasa el cursor sobre una tarjeta de producto (200ms), se descargan los recursos de esa página de detalle.
- Prerender al click down: si el usuario presiona sobre el enlace, se prerenderiza completamente la página.
- Prerender del checkout: la página de checkout se prerenderiza con eagerness moderate, anticipando que un usuario que está mirando productos probablemente acabará comprando.
- Exclusión de productos agotados: no se malgastan recursos precargando páginas de productos sin stock. Un detalle pequeño pero que marca diferencia.
En pruebas reales, sitios de e-commerce han reportado valores de TTFB por debajo de 100ms para navegaciones prerenderizadas, frente a los 136ms habituales para contenido sin cachear. Para ponerlo en perspectiva, es comparable a los 37ms del contenido servido directamente desde CDN.
Depuración y medición
De nada sirve implementar todo esto si no verificas que realmente funciona. Aquí tienes cómo comprobarlo:
Chrome DevTools
- Abre DevTools y ve a la pestaña Application.
- En el panel lateral, busca Speculative loads bajo la sección "Background Services".
- Ahí verás un listado de las reglas activas, las URLs afectadas y su estado (pendiente, en progreso, completada o fallida).
Medir la tasa de acierto
Es buena idea trackear cuántos prerenders se activan realmente frente al total de intentos. Así puedes ajustar tu estrategia de eagerness:
// Registrar cuántos prerenders se activan realmente
if (document.prerendering) {
document.addEventListener("prerenderingchange", () => {
// Esta página se prerenderizó Y se visitó
navigator.sendBeacon("/analytics/prerender-hit", JSON.stringify({
url: window.location.href,
timestamp: Date.now()
}));
});
}
// Comparar con el total de prerenders solicitados
if (HTMLScriptElement.supports?.("speculationrules")) {
navigator.sendBeacon("/analytics/prerender-attempt", JSON.stringify({
url: window.location.href,
timestamp: Date.now()
}));
}
Errores comunes y cómo evitarlos
Después de trabajar con esta API, estos son los fallos que más se repiten:
- Prerenderizar URLs con efectos secundarios: nunca especules sobre URLs como
/logout,/api/delete, o/carrito/vaciar. Parece obvio, pero pasa. Usa exclusiones explícitas connotyhref_matches. - Usar eagerness immediate con document rules: esto puede generar decenas de prerenders simultáneos y comerse toda la memoria y el ancho de banda. Usa
moderateoconservativepara document rules. En serio. - Olvidar proteger las analíticas: sin protección, cada prerender cuenta como una vista de página y tus métricas se van al traste. Siempre implementa la detección con
document.prerendering. - No excluir páginas personalizadas: dashboards, perfiles con datos del usuario... estas páginas pueden causar problemas serios si se prerrenderizan en un contexto diferente.
- Ignorar las limitaciones FIFO: con
moderateyconservative, solo 2 especulaciones están activas a la vez. Las más antiguas se cancelan automáticamente. Tenlo en cuenta al diseñar tu estrategia.
Preguntas frecuentes
¿Es seguro implementar Speculation Rules API si algunos navegadores no la soportan?
Totalmente. Los navegadores que no soportan la API simplemente ignoran el bloque <script type="speculationrules"> sin producir errores. Es mejora progresiva pura: los usuarios de Chrome, Edge y Opera obtienen navegación instantánea, y el resto conserva la experiencia normal sin ninguna penalización.
¿Funciona con aplicaciones de una sola página (SPA)?
No directamente, y esto es algo que conviene tener claro desde el principio. La API está pensada para sitios multi-página (MPAs) donde la navegación implica solicitudes HTTP completas. Si tu app está construida con React, Vue o Angular y usa un router interno, necesitas usar las APIs de prefetch propias de tu framework: el componente <Link prefetch> de Next.js, el prefetch de Nuxt, etc.
¿Cuántos recursos consume el prerender comparado con el prefetch?
Bastante más, la verdad. El prerender descarga todos los sub-recursos, ejecuta el JavaScript completo y renderiza la página en una pestaña invisible en memoria. El prefetch solo descarga el documento HTML principal y algunos recursos críticos. Por eso la recomendación es clara: prefetch para especulaciones de baja confianza, prerender solo para enlaces con alta probabilidad de ser visitados.
¿Cómo afecta el prerender a mis métricas de Google Analytics?
Si usas GA4, estás de suerte: ya es consciente del prerendering y no registra vistas de página hasta que la página prerenderizada se activa. Pero si usas analíticas personalizadas o proveedores que no soportan esta detección, tienes que proteger tus eventos verificando document.prerendering y diferir el envío hasta el evento prerenderingchange.
¿El navegador siempre ejecuta las speculation rules que defino?
No, y esto es importante entenderlo. El navegador tiene la última palabra y puede decidir no ejecutar especulaciones por varias razones: modo de ahorro de energía, poca memoria, conexiones lentas, o porque el usuario desactivó "Precargar páginas" en la configuración. Extensiones como uBlock Origin también las desactivan. Tus reglas son sugerencias inteligentes, no órdenes. El navegador las cumplirá cuando las condiciones sean favorables.