La prioridad implícita y fetchpriority
Qué prioridad asigna el navegador a cada tipo de recurso y por qué, cómo leerla en las herramientas, y cómo ajustarla con el atributo fetchpriority sin romper nada.
El navegador asigna a cada recurso una prioridad de descarga en función de qué es y de dónde aparece en el documento, y esa prioridad decide el orden en que las peticiones salen y compiten por el ancho de banda. Las heurísticas son buenas y no son óptimas en todos los casos, y el atributo fetchpriority existe precisamente para corregirlas donde tú sabes algo que el navegador no puede saber.
- Enumerar los niveles de prioridad y qué recurso cae en cada uno por defecto.
- Explicar por qué las imágenes empiezan con prioridad baja y cuándo suben.
- Leer la prioridad asignada y su cambio en las herramientas de desarrollo.
- Aplicar
fetchprioritysabiendo que es relativo y no absoluto.
La tabla de prioridad implícita
Chrome maneja cinco niveles. Las herramientas de desarrollo los muestran con nombres ligeramente distintos a los internos del motor, y conviene conocer los dos porque la documentación mezcla ambos.
| Nivel interno | Nivel en herramientas | Qué cae aquí por defecto |
|---|---|---|
| VeryHigh | Highest | Documento principal, CSS temprano, fuentes, petición síncrona |
| High | High | Script temprano, fuente precargada, importación, imagen en la ventana tras el layout, petición asíncrona |
| Medium | Medium | CSS tardío, script tardío, las cinco primeras imágenes grandes |
| Low | Low | Script asíncrono o diferido, imágenes en general, vídeo y audio |
| VeryLow | Lowest | CSS cuyo medio no coincide, prefetch |
Hay dos conceptos en esa tabla que hay que definir con precisión porque no son intuitivos.
Temprano y tardío. Un recurso es temprano si se pide antes de que se haya pedido ninguna imagen no precargada; es tardío si se pide después. No es una cuestión de posición en el documento sino de qué ha ocurrido ya. Un script en el <head> es temprano; el mismo script en mitad del <body>, después de tres imágenes, es tardío y baja a prioridad media.
La fase de bloqueo del layout. Los recursos de las dos primeras columnas se cargan durante esa fase; los de las siguientes se cargan de uno en uno mientras la fase dura, para que el navegador pueda concentrarse en lo importante. Esa es la diferencia práctica entre estar en High y estar en Medium: no es solo el orden, es cuántos pueden bajar a la vez.
El caso de las imágenes
Las imágenes merecen su propio apartado porque su tratamiento explica el problema que fetchpriority vino a resolver.
Las imágenes empiezan en prioridad baja. Siempre, todas. La razón es sensata: el navegador no sabe cuáles son importantes y las imágenes no bloquean el renderizado, así que por defecto no deben competir con el CSS ni con los scripts.
En el momento del layout, las que están dentro de la ventana suben a prioridad alta. El navegador ya sabe qué se ve, así que corrige.
El problema es el retraso entre las dos cosas. Entre que se descubre la imagen y se completa el layout pueden pasar cientos de milisegundos, y durante todo ese tiempo la imagen del área visible, que muy probablemente es tu elemento principal, está en la cola de las prioridades bajas.
Chrome introdujo una mitigación automática: desde la versión 117, las cinco primeras imágenes grandes, entendiendo por grandes las que superan los diez mil píxeles cuadrados, se sitúan en prioridad media desde el principio, y dos de ellas pueden descargarse en paralelo durante la fase inicial más restrictiva. Ayuda, y sigue siendo una heurística que adivina.
Con fetchpriority="high" en la imagen, esta arranca directamente en prioridad alta, sin esperar al layout ni depender de que la heurística acierte.
El experimento publicado sobre la página de un buscador de vuelos, reescribiendo la página para añadir prioridad alta a la imagen principal, midió una mejora del LCP de 2,6 a 1,9 segundos. Setecientos milisegundos por un atributo. No es representativo de todos los sitios, y sí ilustra la magnitud de lo que se pierde esperando al layout.
Leer la prioridad
En el panel de red de las herramientas de desarrollo, la columna de prioridad no aparece por defecto: hay que activarla haciendo clic derecho en las cabeceras de la tabla y marcándola.
Cuando una prioridad cambia durante la carga, se puede ver tanto la inicial como la final activando la vista de filas grandes o pasando el ratón por encima. Ese par de valores es la información más útil del panel para este tema: si tu imagen principal aparece como media al principio y alta después, estás viendo exactamente el retraso que hay que eliminar.
Desde JavaScript no hay una API que exponga la prioridad asignada. Lo que sí se puede observar es su consecuencia, que es el orden de inicio de las peticiones:
// Orden real en que arrancaron las peticiones.
performance.getEntriesByType('resource')
.sort((a, b) => a.startTime - b.startTime)
.slice(0, 20)
.forEach((r, i) => {
console.log(
String(i + 1).padStart(2),
String(Math.round(r.startTime)).padStart(5) + 'ms',
r.initiatorType.padEnd(8),
r.name.split('/').pop().slice(0, 45),
);
});
Si tu recurso crítico no está entre los primeros de esa lista, tienes un problema de prioridad, de descubrimiento, o de los dos.
El atributo fetchpriority
Toma tres valores: high, low y auto, que es el valor por defecto.
<!-- Sube la prioridad de la imagen principal. -->
<img src="/img/hero.avif" fetchpriority="high" width="1200" height="600" alt="">
<!-- Baja la de las imagenes del carrusel que no se ven todavia. -->
<img src="/img/carrusel-2.avif" fetchpriority="low" alt="">
<!-- Script asincrono que si importa. -->
<script src="/js/interactivo.js" async fetchpriority="high"></script>
<!-- Precarga que no debe competir con lo critico. -->
<link rel="preload" as="script" href="/js/secundario.js" fetchpriority="low">
Y su equivalente en la API de obtención de recursos, para datos:
// Datos criticos con la prioridad alta por defecto.
const usuario = await fetch('/api/usuario');
// Datos secundarios que no deben competir con los anteriores.
const sugerencias = await fetch('/api/sugerencias', { priority: 'low' });
Tres propiedades del atributo que hay que tener claras.
Es relativo, no absoluto. No fija la prioridad en alta o baja: la sube o la baja una cantidad apropiada desde su valor por defecto. Un CSS temprano con prioridad alta se queda en el nivel máximo, porque ya estaba ahí; el mismo CSS con prioridad baja solo baja un escalón, no cae al fondo. Esperar que el atributo fije un valor concreto lleva a conclusiones equivocadas al medir.
Es una pista, no una orden. El navegador intenta respetarla y puede aplicar sus propias preferencias para resolver conflictos.
No ayuda al descubrimiento. Cambia cómo se descarga un recurso cuando se descarga, no cuándo se entera el navegador de que existe. Si tu imagen está escondida detrás de una hoja de estilos, ningún valor de prioridad la va a hacer aparecer antes: eso lo resuelve una precarga.
El soporte llegó en Chrome y Edge 102, Safari 17.2 y Firefox 132. En un navegador que no lo entienda, el atributo se ignora sin efectos secundarios.
Cuando ajustas la prioridad de un recurso ocurren dos cosas. Una es que el navegador reordena internamente su propia cola: decide cuáles pide antes y cuáles retiene mientras procesa la cabecera del documento. La otra es que comunica esa prioridad al servidor a través del protocolo, para que este decida en qué orden entregar los bytes de los flujos multiplexados. La primera funciona siempre y es la que produce la mayor parte del beneficio. La segunda depende por completo de que el servidor y la CDN implementen la priorización, y está documentado que las CDN no lo hacen de manera uniforme ni en HTTP/2 ni en HTTP/3. La consecuencia práctica es doble y ahorra frustración. Primero, merece la pena usar fetchpriority aunque tu CDN ignore la priorización del protocolo, porque la reordenación interna del navegador sigue actuando. Y segundo, si mides el efecto y sale menor de lo esperado, antes de concluir que el atributo no sirve, comprueba qué hace tu proveedor con la priorización: es una diferencia de comportamiento entre proveedores que puede explicar por qué la misma optimización funciona en un sitio y no en otro.