wandres.dev
PRIORIDADES DE RECURSOS · preload, preconnect, fetchpriority

preconnect y dns-prefetch

Las dos pistas que adelantan el establecimiento de conexión a un origen ajeno, cuánto ahorran realmente, y por qué más de tres o cuatro empeoran la situación.

⏱ 16 min

Cuando un recurso crítico vive en un dominio ajeno, la cadena de establecimiento cuesta tres o cuatro viajes de ida y vuelta que no se solapan con nada, porque no empiezan hasta que se descubre el recurso. preconnect adelanta esa cadena al principio de la carga para que se solape con otro trabajo. Es una de las pistas más rentables y también una de las que más fácil es sobreutilizar.

🎯 Al terminar esta lección sabrás
  • Explicar qué hace exactamente cada una de las dos pistas y en qué se diferencian.
  • Calcular cuánto ahorra una preconexión en una red concreta.
  • Aplicar el atributo de origen cruzado correctamente y saber por qué importa.
  • Reconocer los tres casos en que estas pistas hacen daño.

Qué hace cada una

dns-prefetch resuelve el nombre del dominio por adelantado. Solo eso. Ahorra la parte de DNS de la cadena, que es la más barata de las tres.

<link rel="dns-prefetch" href="https://cdn.ejemplo.com">

preconnect hace la cadena completa: resuelve el DNS, abre la conexión de transporte y negocia el cifrado. Deja la conexión lista para que la primera petición a ese origen no pague nada de establecimiento.

<link rel="preconnect" href="https://cdn.ejemplo.com" crossorigin>

La diferencia en coste ahorrado es grande:

Pista Qué adelanta Ahorro típico con RTT de 150 ms
dns-prefetch Resolución de nombre 20 a 120 ms
preconnect Nombre, conexión y cifrado 300 a 450 ms

La diferencia en coste pagado también lo es, y es la razón por la que no se ponen a mansalva. Una preconexión mantiene abierta una conexión con su estado en el cliente y en el servidor, y consume recursos aunque nunca se use. Una resolución de nombre es prácticamente gratis.

El atributo de origen cruzado

Este es el detalle que más veces se pone mal y que anula silenciosamente la mitad del beneficio.

Los navegadores particionan las conexiones según si la petición se va a hacer en modo anónimo o con credenciales. Una conexión abierta para peticiones con credenciales no se reutiliza para peticiones anónimas, y viceversa.

Como consecuencia, una preconexión con el modo equivocado abre una conexión que no sirve para nada: el navegador tendrá que abrir otra cuando llegue la petición real, y habrás pagado el establecimiento dos veces.

La regla es esta:

<!-- Fuentes: siempre en modo anonimo. Lleva crossorigin. -->
<link rel="preconnect" href="https://fuentes.ejemplo.com" crossorigin>

<!-- Imagenes servidas sin credenciales: lleva crossorigin. -->
<link rel="preconnect" href="https://imagenes.ejemplo.com" crossorigin>

<!-- API a la que llamas con cookies: NO lleva crossorigin. -->
<link rel="preconnect" href="https://api.ejemplo.com">

Las fuentes son el caso más importante y el que más se equivoca: se piden siempre en modo anónimo, sin credenciales, por especificación. Una preconexión a un dominio de fuentes sin el atributo abre la conexión equivocada.

Si no estás seguro de qué modo usa un recurso, la comprobación es directa: mira en los tiempos de recurso si su petición reutilizó conexión.

// Si connectStart y connectEnd difieren, hubo establecimiento nuevo:
// la preconexion no sirvio.
performance.getEntriesByType('resource')
  .filter((r) => new URL(r.name).origin !== location.origin)
  .forEach((r) => {
    const reutilizada = r.connectEnd === r.connectStart;
    console.log(
      reutilizada ? 'REUTILIZADA' : 'NUEVA      ',
      Math.round(r.connectEnd - r.connectStart) + 'ms',
      new URL(r.name).origin,
    );
  });
⚠️
Una preconexión no usada se descarta pronto

El navegador cierra las conexiones abiertas por una preconexión si no se usan en un plazo corto, del orden de diez segundos. Eso significa que precenectar a un origen que solo se va a usar mucho más tarde no sirve de nada: la conexión se habrá cerrado. Precenecta solo a orígenes que vas a usar en la carga inicial.

Cuánto ahorra de verdad

El ahorro no es siempre el valor de la tabla, porque depende de que la preconexión se solape con otro trabajo.

En el mejor caso, la etiqueta de preconexión está al principio del <head> y el recurso se descubre mucho después, por ejemplo porque está referenciado desde el CSS. Entonces el establecimiento ocurre mientras se descarga y parsea el CSS, y cuando llega el momento de pedir el recurso la conexión ya está lista: ahorras la cadena entera.

En el peor caso, la preconexión y el descubrimiento del recurso ocurren casi a la vez. Entonces no has adelantado nada y solo has añadido una etiqueta.

Y hay un caso intermedio muy frecuente: la preconexión se ejecuta a la vez que otras peticiones críticas y compite con ellas por el ancho de banda y por los recursos del cliente. En una red muy limitada, el saludo de cifrado de una preconexión puede retrasar la descarga del CSS.

Por eso el orden dentro del <head> importa. La secuencia razonable es: metadatos de codificación y ventana, preconexiones a orígenes inevitables, hoja de estilos crítica, precarga del recurso principal.

Los tres casos en que hacen daño

Uno: demasiadas preconexiones. Cada una abre una conexión completa con su coste en ambos extremos. La recomendación estándar es limitarlas a tres o cuatro orígenes como máximo, y solo a los que de verdad se usan en la carga inicial. Con diez preconexiones estás abriendo diez conexiones que compiten entre sí justo en el momento en que el ancho de banda es más escaso.

Dos: precenectar a un origen que ya se usa pronto. Si el recurso se descubre en el HTML en las primeras líneas, el navegador ya va a abrir la conexión de inmediato. La preconexión no adelanta nada y solo añade bytes al documento.

Tres: el modo de credenciales equivocado. Abres una conexión que no se reutiliza y pagas dos establecimientos en lugar de uno. Es peor que no haber puesto nada.

Un cuarto caso que no es daño pero sí desperdicio: usar dns-prefetch cuando podías usar preconnect. Si vas a usar el origen con seguridad, la preconexión ahorra mucho más por un coste marginal. La pista de resolución de nombre tiene sentido para orígenes que puede que uses, como una lista de posibles proveedores de contenido, donde una conexión completa a cada uno sería excesiva.

La estrategia completa

Un ejemplo realista de cabecera para una página que usa un dominio de imágenes, uno de fuentes y una API.

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Origenes inevitables en la carga inicial: como maximo tres o cuatro. -->
  <link rel="preconnect" href="https://imagenes.ejemplo.com" crossorigin>
  <link rel="preconnect" href="https://fuentes.ejemplo.com" crossorigin>

  <!-- Origen que puede que se use, y que no merece una conexion completa. -->
  <link rel="dns-prefetch" href="https://analitica.ejemplo.com">

  <link rel="stylesheet" href="/css/critico.css">
  <title>Titulo</title>
</head>

Y la alternativa que casi siempre es mejor y que conviene considerar antes de escribir ninguna de estas etiquetas: eliminar el origen ajeno. Servir las fuentes y las imágenes desde tu propio dominio no ahorra 300 milisegundos, ahorra la cadena entera y aprovecha una conexión cuya ventana de congestión ya ha crecido. La preconexión es la solución cuando el origen ajeno es inevitable, no la primera opción.

La preconexión que más se pone y que casi nunca hace falta es la del propio origen

Es sorprendentemente común encontrar una etiqueta de preconexión apuntando al mismo dominio desde el que se sirve la página. No hace nada: la conexión al propio origen ya está abierta por definición, porque es la que trajo el documento. En el mejor caso es una línea inerte; en el peor, con el atributo de origen cruzado puesto, abre una segunda conexión al mismo servidor en el modo de credenciales contrario, que no se va a reutilizar para nada y que fragmenta el tráfico justo en el protocolo diseñado para multiplexar todo por una sola. He visto esa combinación exacta en plantillas de proyecto copiadas cientos de veces, propagándose porque nadie la cuestiona: no rompe nada visible y añade un establecimiento completo en la primera visita de cada usuario. La comprobación es de diez segundos: busca en tu HTML las etiquetas de preconexión y comprueba que ninguna apunta a tu propio origen. Y si tu documento y tus estáticos viven en dominios distintos, plantéate si esa separación sigue justificándose, porque en un mundo de multiplexación y de caché particionada por sitio ya casi nunca lo hace.