Cuándo las pistas hacen daño
La aritmética que explica por qué un preload de más retrasa el LCP, los seis modos de fallo con su síntoma, y el guion de auditoría que los encuentra en treinta segundos.
Ninguna pista de recursos crea ancho de banda. Todas reordenan el reparto de un ancho de banda fijo, y reordenar significa que algo pasa delante y algo pasa detrás. Una precarga acertada adelanta el recurso que decide la métrica; una precarga equivocada adelanta otro y retrasa el que importaba, con la particularidad de que en el informe queda registrada como una optimización.
- Calcular con números el retraso que una precarga innecesaria impone al recurso crítico.
- Reconocer los seis modos de fallo por su síntoma en el panel de red.
- Auditar todas las pistas de una página con un guion y detectar las inútiles.
- Aplicar un criterio de decisión que no dependa de la intuición.
La aritmética que nadie hace
El argumento a favor de una precarga siempre suena bien: “este recurso es importante, así que lo pido antes”. El argumento en contra no suena, porque exige una multiplicación.
Toma la conexión que Lighthouse usa para simular un móvil: 1,6 Mbit/s de bajada y 150 ms de ida y vuelta. Eso son 200 KB por segundo de capacidad real, y no es un caso pesimista: es la referencia con la que se puntúa tu sitio.
Tu imagen del elemento principal pesa 120 KB. Sola en la red, tarda 600 ms.
Ahora añades una precarga de la fuente de la tipografía de los titulares, que pesa 100 KB, y que sale con prioridad alta porque las fuentes van en prioridad alta. Las dos peticiones se reparten el canal. La fuente termina a los 1.000 ms; en ese momento la imagen lleva 100 KB descargados y le faltan 20, que a plena capacidad tarda 100 ms más. La imagen termina a los 1.100 ms en lugar de a los 600.
Quinientos milisegundos de regresión en el LCP por una etiqueta que en la revisión de código pasó como mejora. Y la fuente, que ahora está en el dispositivo medio segundo antes, no cambia nada visible si el texto ya se estaba pintando con la fuente de reserva.
Esa cuenta es toda la lección. La pregunta correcta antes de escribir una precarga no es si el recurso es importante, sino si es más importante que aquello a lo que le va a quitar el sitio. En una página bien hecha, la respuesta suele ser que no, porque lo que hay compitiendo es el CSS bloqueante y la imagen del hero.
Los seis modos de fallo
Uno: la precarga que compite con el recurso crítico. El caso de la aritmética de arriba. Síntoma en el panel de red: dos o más peticiones en prioridad alta que arrancan a la vez y se alargan las dos. Corrección: quitar la precarga o bajarla con fetchpriority="low".
Dos: la descarga duplicada por desajuste. El navegador solo reutiliza una precarga si coinciden URL, destino, modo de credenciales y política de referente. Falla en tres sitios con nombre propio: fuentes sin crossorigin, precargas sin as, y precargas de imagen con href fijo cuando la imagen real usa srcset y elige otro archivo. Síntoma: el mismo recurso aparece dos veces en el panel de red. Es el fallo más caro porque duplica los bytes exactos del recurso que querías acelerar.
Tres: la precarga que nunca se usa. Queda un resto de una refactorización, o apunta a una URL con hash antiguo. El navegador te lo dice por consola: avisa de que un recurso se precargó y no se usó en los primeros segundos tras el evento de carga. Ese mensaje se ignora sistemáticamente y es de los pocos avisos del navegador que señalan una regresión medible.
Cuatro: la precarga redundante. El recurso ya estaba escrito como atributo en el HTML y el escáner lo había descubierto en el mismo instante. No ahorra nada. Y si el recurso es una imagen, la precarga además le sube la prioridad, con lo que puede colarse por delante del CSS bloqueante y retrasar el primer pintado.
Cinco: la inflación de prioridades. Cuando marcas seis imágenes con fetchpriority="high", no has priorizado seis imágenes: has desactivado la priorización. El navegador reparte el canal entre las seis y ninguna llega antes que si no hubieras tocado nada. La documentación de optimización del elemento principal lo dice sin rodeos: poner prioridad alta en más de una o dos imágenes deja de servir para reducir el LCP.
Seis: la anticipación que gasta los datos del usuario. Un prefetch agresivo en una conexión medida es una factura que paga otro. En Chromium puedes consultar la preferencia de ahorro de datos y el tipo de conexión efectivo antes de decidir, aunque la API no existe en los demás motores y hay que tratar su ausencia como “no lo sé”:
// Solo en Chromium. Si no existe, no anticipes de forma agresiva.
const c = navigator.connection;
const anticipar = c
? !c.saveData && ['4g'].includes(c.effectiveType)
: false;
if (anticipar) {
const l = document.createElement('link');
l.rel = 'prefetch';
l.href = '/js/panel.js';
document.head.append(l);
}
El guion de auditoría
Este guion cruza las pistas declaradas en el HTML con lo que de verdad pasó por la red. Pégalo en la consola de la página ya cargada, con la caché desactivada:
// Auditoria de pistas de recursos. Ejecutar tras el evento de carga.
const pistas = [...document.querySelectorAll(
'link[rel="preload"], link[rel="modulepreload"], link[rel="prefetch"], link[rel="preconnect"]',
)].map((l) => ({
rel: l.rel,
as: l.getAttribute('as') || (l.rel === 'modulepreload' ? 'script' : ''),
url: l.href ? new URL(l.href, location.href).href : '',
crossorigin: l.hasAttribute('crossorigin'),
}));
const recursos = performance.getEntriesByType('resource');
const cuenta = recursos.reduce((m, r) => m.set(r.name, (m.get(r.name) || 0) + 1), new Map());
// El HTML tal como llego, para detectar precargas redundantes.
const htmlBruto = await fetch(location.href, { cache: 'reload' }).then((r) => r.text());
const apariciones = (texto, aguja) => texto.split(aguja).length - 1;
for (const p of pistas) {
const veces = cuenta.get(p.url) || 0;
const ruta = p.url.replace(location.origin, '');
const problemas = [];
if (p.rel === 'preload' && !p.as) problemas.push('SIN as: doble descarga segura');
if (p.as === 'font' && !p.crossorigin) problemas.push('FUENTE sin crossorigin: doble descarga');
if (p.rel !== 'preconnect' && veces === 0) problemas.push('NUNCA USADA: bytes tirados');
if (veces > 1) problemas.push(`DUPLICADA: ${veces} descargas`);
// La propia etiqueta de precarga ya cuenta una aparicion.
// Dos o mas significa que hay ademas una referencia que el escaner ve.
if (p.rel === 'preload' && apariciones(htmlBruto, ruta) > 1)
problemas.push('REDUNDANTE: el escaner ya la descubre');
console.log(problemas.length ? '[X]' : '[ok]', p.rel, p.as, ruta, problemas.join(' | '));
}
El mismo recuento de apariciones, desde el terminal y sin abrir el navegador:
# Mas de una aparicion significa que ademas de la precarga hay una
# referencia real, que el escaner habria descubierto por su cuenta.
curl -s https://ejemplo.com | grep -o '/img/hero.avif' | wc -l
Las tres comprobaciones que valen más que todo lo anterior, y que se hacen sin escribir nada:
- Ordena el panel de red por prioridad y mira cuántas peticiones hay en prioridad alta durante el primer segundo. Si son más de tres o cuatro, no tienes prioridades, tienes una cola.
- Ordena por nombre y busca archivos repetidos. Cada repetición es un desajuste.
- Filtra por la columna de tamaño y comprueba que el recurso más grande del primer segundo es el que decide tu métrica. Si el archivo más gordo de la ventana crítica es una fuente y tu elemento principal es una imagen, tienes el orden invertido.
Una precarga solo puede ayudar cuando el recurso hay que descargarlo. Si mides recargando la página con la caché activa, el recurso llega de disco en cuatro milisegundos con precarga y sin ella, y concluirás que da igual. Y si mides en tu red de oficina con cien megas, la competencia por el ancho de banda que produce el daño no ocurre, así que tampoco verás el perjuicio de una precarga sobrante. Las dos direcciones del error se corrigen igual: caché desactivada y limitación de red activa, siempre. Sin las dos cosas, cualquier conclusión sobre pistas de recursos es ruido.
El criterio de decisión
Cuatro preguntas, en orden. Si una falla, no pongas la pista.
¿El escáner de precarga puede ver este recurso en el HTML en bruto? Si la respuesta es sí, no hace falta precargarlo: ya está descubierto. Si quieres cambiar su orden, usa fetchpriority.
¿Este recurso hace falta para la primera pantalla? Si la respuesta es no, una precarga solo puede quitarle sitio a lo que sí hace falta. Para lo que viene después existe prefetch, con la prioridad mínima, o una carga diferida al hueco de inactividad.
¿Es más importante que el elemento principal de la página? Casi nunca. La imagen o el bloque de texto que define el LCP es lo que el usuario está esperando; todo lo demás va detrás por definición.
¿Lo he medido con la caché fría y la red limitada, antes y después? Si no, no sabes si ayuda. Una pista de recursos es una hipótesis sobre el orden óptimo, y las hipótesis se comprueban.
La regla que resume las cuatro cabe en una línea: precarga lo que el navegador no puede descubrir y que además necesitas ya. La conjunción es obligatoria. Un recurso descubrible no necesita precarga; un recurso no urgente no la merece.
Cuando abres el <head> de un sitio y te encuentras nueve preconexiones, seis precargas y cuatro anticipaciones, lo que estás viendo no es un sitio muy optimizado: es la sedimentación de tres años de auditorías automáticas seguidas al pie de la letra por personas distintas. Cada etiqueta se añadió para cerrar una recomendación de un informe, nadie borró ninguna, y el conjunto se comporta de una forma que ninguno de sus autores previó, porque todas compiten entre sí durante los mismos dos segundos. He visto más de una vez que borrar la mitad de esas etiquetas mejora el LCP, y el motivo es exactamente el de la aritmética del principio: al reducir el número de peticiones en vuelo con prioridad alta, el recurso que decide la métrica deja de repartir el canal con cinco competidores. La forma correcta de tratar estas etiquetas no es como líneas de configuración que se acumulan, sino como presupuesto: tres o cuatro preconexiones, una o dos precargas, y cada una con un dueño que sepa decir qué recurso concreto adelanta y contra qué compite. Si nadie sabe responder eso de una etiqueta, esa etiqueta es candidata a borrarse, y borrarla es una intervención tan legítima como añadirla. La prueba es barata y la haces en una tarde: quita todas las pistas, mide, y vuelve a añadir solo aquellas cuya eliminación empeoró el número.