Preload, preconnect et fetchpriority : comment dire au navigateur ce qui compte vraiment

dns-prefetch, preconnect, preload, prefetch et fetchpriority : comment le navigateur décide des priorités, quand chaque hint accélère la page et quand il la ralentit, avec des exemples WordPress.

Un navigateur qui reçoit des ressources depuis une file de requêtes, avec l’image hero verte qui passe devant les autres avec une priorité haute

Un navigateur qui charge une page doit prendre des décisions en permanence : quelle ressource télécharger en premier, laquelle peut attendre, avec quels serveurs ouvrir une connexion. Il le fait avec des heuristiques sophistiquées, fondées sur le type de ressource et sa position dans le document, mais il ne connaît pas la page comme celui qui l’a construite. Il ne sait pas que cette image est la plus importante, que cette police est nécessaire au premier affichage, que ce domaine tiers va être contacté dans un instant.

Les resource hints et l’attribut fetchpriority existent pour combler cet écart : ils permettent au développeur de dire au navigateur ce qui compte vraiment. Bien utilisés, ils font avancer les ressources critiques et font gagner des centaines de millisecondes sur le LCP. Mal utilisés, et cela arrive souvent, ils mettent des ressources inutiles en concurrence avec les importantes et ralentissent exactement ce qu’ils devaient accélérer. Dans cet article, nous les passons en revue un par un, avec les cas où ils aident et ceux où ils font des dégâts.

Comment le navigateur décide des priorités

Pour comprendre quand un hint est utile, il faut savoir ce que fait le navigateur sans lui. Pendant la lecture du HTML, un composant appelé preload scanner parcourt le document en avance et repère les ressources à télécharger : feuilles de style, scripts, images. Chacune reçoit une priorité fondée sur son type : les feuilles de style dans le head ont la priorité la plus haute, car elles bloquent le rendu ; les scripts synchrones une priorité haute ; les polices une priorité haute, mais elles ne sont découvertes que lorsque le CSS les demande ; les images, par défaut, une priorité basse.

Les images sont le cas le plus délicat. Le navigateur ne sait pas lesquelles seront visibles tant qu’il n’a pas calculé la mise en page, et jusque-là il les traite avec une priorité basse, avec quelques exceptions pour les premières grandes images du document dans les versions récentes de Chrome. Ce n’est qu’après la mise en page, quand il découvre qu’une image se trouve sur l’écran initial, qu’il relève sa priorité. Pour l’image principale d’une page, qui est presque toujours l’élément LCP, cela veut dire partir tard et derrière d’autres ressources.

Il y a ensuite des ressources que le preload scanner ne voit pas du tout : les polices déclarées dans le CSS, les images d’arrière-plan définies dans les feuilles de style, les scripts insérés par d’autres scripts, les ressources demandées par JavaScript. Le navigateur ne les découvre qu’en arrivant au point où elles sont utilisées, souvent plusieurs centaines de millisecondes après le début du chargement.

Tableau des cinq hints : dns-prefetch résout le nom de domaine, pour les domaines tiers utilisés plus tard, coût minimal ; preconnect ouvre DNS, TCP et TLS à l’avance, pour deux à quatre domaines critiques, coût faible ; preload télécharge une ressource tout de suite, pour les polices, les images hero dans le CSS et les ressources découvertes tardivement, coût élevé en cas d’erreur ; fetchpriority modifie la priorité d’une requête, pour l’image LCP et les images secondaires, aucun coût ; prefetch télécharge pour la page suivante, pour les navigations très probables, risque de bande passante gaspillée
Chaque hint avance une partie différente du travail du navigateur.

dns-prefetch et preconnect : préparer le terrain

Avant de télécharger une ressource depuis un nouveau domaine, le navigateur doit résoudre son nom via le DNS, ouvrir une connexion TCP et négocier le chiffrement TLS. Sur un réseau mobile, ces étapes peuvent prendre de 100 à 300 millisecondes, et elles se répètent pour chaque domaine différent : le CDN d’images, le service de polices, la plateforme d’analytics, le système de paiement.

preconnect demande au navigateur d’effectuer toutes ces étapes tout de suite, pour que la connexion soit prête quand la ressource sera demandée. dns-prefetch ne fait que la première partie, la résolution du nom : il coûte moins et peut être utilisé pour davantage de domaines.

<!-- Connexion complète : pour les domaines critiques pour le premier affichage -->
<link rel="preconnect" href="https://cdn.example.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- DNS seulement : pour les domaines utilisés plus tard -->
<link rel="dns-prefetch" href="https://www.googletagmanager.com">

Preconnect a du sens pour quelques domaines, deux à quatre, d’où arrivent des ressources nécessaires au premier affichage : le domaine des images s’il diffère de celui du site, le service de polices, un CDN de ressources statiques. Ouvrir à l’avance des connexions vers dix domaines produit l’effet inverse : le navigateur et l’appareil travaillent sur des connexions qui seront peut-être utilisées dans plusieurs secondes, ou jamais, et qui entre-temps risquent d’être fermées pour inactivité. L’attribut crossorigin compte : les polices sont demandées en mode CORS, qui utilise une connexion distincte, et un preconnect sans crossorigin vers le domaine des polices prépare la mauvaise connexion.

La meilleure façon d’économiser ces étapes, cependant, est de ne pas en avoir besoin : héberger les polices et les ressources statiques sur le même domaine que le site, comme nous l’avons vu à propos du subsetting des polices, élimine entièrement les connexions supplémentaires.

preload : télécharger tout de suite ce qui serait découvert tard

preload demande au navigateur de télécharger immédiatement une ressource précise, avec une priorité haute, même s’il ne l’a pas encore rencontrée dans le document. C’est l’outil pour les ressources que le preload scanner ne voit pas et qui sont nécessaires tôt : la police principale déclarée dans le CSS, l’image hero définie en arrière-plan dans une feuille de style, le script chargé dynamiquement par un autre script mais essentiel au premier affichage.

<!-- Police utilisée dans le texte au-dessus de la ligne de flottaison -->
<link rel="preload" href="/fonts/plex-400.woff2" as="font" type="font/woff2" crossorigin>

<!-- Image hero définie en arrière-plan CSS, avec variantes responsives -->
<link rel="preload" as="image" href="/media/hero-1600.webp"
	imagesrcset="/media/hero-800.webp 800w, /media/hero-1600.webp 1600w"
	imagesizes="100vw" fetchpriority="high">

Quelques détails font la différence entre un preload utile et un preload nuisible. L’attribut as est obligatoire : il indique le type de ressource, détermine la priorité et permet au navigateur de réutiliser la ressource téléchargée quand il la rencontre dans le document. Les polices exigent crossorigin, même sur le même domaine, sinon le navigateur télécharge la police deux fois. Pour les images responsives, imagesrcset et imagesizes font choisir au navigateur la même variante que choisira l’élément image ; un preload à taille unique risque de télécharger un fichier qui ne sera ensuite pas utilisé.

Le preload ne doit pas servir pour des ressources que le navigateur trouve déjà tout seul à temps. Une image normale dans le HTML, avec src et fetchpriority, n’a pas besoin de preload : le preload scanner la trouve tout de suite. Une feuille de style dans le head non plus. Le preload sert là où il y a une découverte tardive à éliminer, et non comme manière générique de dire « ceci est important ».

Le problème de l’excès

Le preload est aussi l’outil le plus détourné. Les plugins d’optimisation, les thèmes et les configurations copiées d’autres projets finissent souvent par précharger des dizaines de ressources : toutes les polices dans toutes les variantes, des scripts, des feuilles de style secondaires, des images qui ne sont pas au-dessus de la ligne de flottaison. Comme chaque preload reçoit une priorité haute, le résultat est une compétition dans laquelle rien n’est vraiment prioritaire : la feuille de style principale et l’image LCP partagent la bande passante avec des ressources qui seront nécessaires plus tard ou pas du tout. Chrome signale dans la console les ressources préchargées et non utilisées au bout de quelques secondes : c’est le premier contrôle à faire sur un site qui compte beaucoup de preloads.

fetchpriority : changer la priorité sans avancer la découverte

L’attribut fetchpriority est le plus récent et, pour le LCP, souvent le plus efficace. Il n’avance pas la découverte d’une ressource : il change sa priorité une fois découverte. Il peut valoir high, low ou auto, et s’applique aux images, aux scripts, aux feuilles de style, aux iframes et aux preloads. Il est pris en charge par tous les principaux navigateurs dans leurs versions récentes ; ceux qui ne le prennent pas en charge l’ignorent.

Le principal cas d’usage est l’image LCP. Avec fetchpriority="high", le navigateur la télécharge avec une priorité haute dès le départ, au lieu d’attendre que la mise en page lui révèle qu’elle est visible. Le gain peut atteindre plusieurs centaines de millisecondes, avec un seul mot ajouté au balisage, comme l’explique l’article sur le LCP.

Trois chargements comparés : avec la priorité par défaut, l’image hero démarre avec une priorité basse et ne monte qu’après la mise en page, LCP 2,3 secondes ; avec fetchpriority high, elle démarre tout de suite avec une priorité haute, LCP 1,4 seconde ; avec huit preloads simultanés, les ressources entrent en concurrence, le CSS ralentit et le LCP monte à 2,6 secondes
La bonne priorité avance ce qui compte ; trop de preloads ralentissent tout.
<!-- L’image principale : priorité haute, pas de lazy loading -->
<img src="/media/copertina.webp" width="1200" height="630"
	fetchpriority="high" alt="…">

<!-- Les diapositives masquées d’un carrousel : priorité basse -->
<img src="/media/slide-2.webp" width="1200" height="630"
	fetchpriority="low" alt="…">

<!-- Un script non critique : priorité basse -->
<script src="/js/widget.js" async fetchpriority="low"></script>

La valeur low est aussi utile que high, et bien moins utilisée. Les images d’un carrousel qui ne sont pas visibles au départ, une grande image secondaire au-dessus de la ligne de flottaison, un script async qui n’est pas nécessaire au premier affichage : baisser leur priorité laisse davantage de bande passante aux ressources qui comptent. Les requêtes faites avec fetch() acceptent elles aussi une option de priorité, utile pour distinguer les données nécessaires tout de suite de celles qui peuvent attendre.

Ici aussi, la règle de la parcimonie s’applique : fetchpriority="high" sur une image par page, celle du LCP, deux au maximum. L’attribuer à toutes les images visibles revient à ne l’attribuer à aucune. Et il ne faut jamais le combiner avec loading="lazy", qui reporte le chargement après la mise en page et annule l’effet, comme nous l’avons vu dans l’article sur le lazy loading.

prefetch et modulepreload

prefetch demande au navigateur de télécharger, avec une priorité très basse et pendant les temps morts, une ressource qui sera probablement nécessaire sur la page suivante. Pendant longtemps, ce fut le seul moyen d’anticiper les navigations ; aujourd’hui, pour des pages entières, les Speculation Rules offrent un bien meilleur contrôle, avec prefetch et prerender pilotés par le comportement de l’utilisateur. Le prefetch de ressources isolées reste utile dans des cas précis, comme la feuille de style de la page de commande préchargée depuis le panier.

modulepreload est la version de preload pour les modules JavaScript : il télécharge et analyse un module à l’avance et, selon le navigateur, ses dépendances. Il est utile dans les applications qui utilisent les modules natifs, où la chaîne d’imports serait sinon découverte une étape à la fois.

Les hints dans les en-têtes HTTP

Preload et preconnect peuvent aussi être envoyés sous forme d’en-têtes HTTP Link, au lieu d’éléments dans le HTML. L’avantage est que le navigateur les reçoit avec les en-têtes, avant même de lire le document, et qu’ils peuvent être gérés par le serveur ou le CDN sans modifier les templates. Ils sont aussi à la base des Early Hints, par lesquels le serveur communique les mêmes hints pendant que la page est encore en préparation.

Link: <https://cdn.example.com>; rel=preconnect
Link: </fonts/plex-400.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin

Le cas de WordPress

WordPress gère déjà certains de ces hints. Depuis la version 6.3, il ajoute automatiquement fetchpriority="high" à l’image qu’il estime être l’élément LCP et n’applique pas de lazy loading aux premières images. Pour les preconnects et les dns-prefetch, il y a le filtre wp_resource_hints, et pour les preloads le filtre wp_preload_resources, disponible depuis la version 6.1 :

// Preconnect vers le CDN d’images
add_filter(
	'wp_resource_hints',
	function ( $urls, $type ) {
		if ( 'preconnect' === $type ) {
			$urls[] = 'https://cdn.example.com';
		}
		return $urls;
	},
	10,
	2
);

// Preload de la police principale du thème
add_filter(
	'wp_preload_resources',
	function ( $resources ) {
		$resources[] = array(
			'href'        => get_theme_file_uri( 'fonts/plex-400.woff2' ),
			'as'          => 'font',
			'type'        => 'font/woff2',
			'crossorigin' => true,
		);
		return $resources;
	}
);

L’estimation automatique de l’image LCP fonctionne bien avec les thèmes qui utilisent les fonctions d’image standard, mais elle peut se tromper avec les page builders, les sliders et les blocs personnalisés. Et de nombreux plugins d’optimisation ajoutent leurs propres preloads et preconnects, parfois en doublon avec ceux du thème ou du cœur. Le résultat doit donc toujours être vérifié dans le code source de la page réelle.

Comment vérifier

Dans le panneau Network des DevTools, on peut ajouter la colonne Priority, qui montre la priorité de chaque requête et, avec les réglages avancés, la priorité initiale également. C’est le moyen le plus direct de voir si l’image LCP démarre avec une priorité haute et quand elle est demandée par rapport aux autres ressources. La console signale les preloads inutilisés et les preconnects inutilisés. PageSpeed Insights, dans le diagnostic du LCP, indique si l’image a été découverte tard et si elle aurait profité de fetchpriority ou d’un preload, et montre quels domaines mériteraient un preconnect.

Un bon exercice consiste à comparer le graphique en cascade avant et après chaque modification : un hint efficace déplace visiblement vers la gauche la ressource à laquelle il s’applique sans déplacer vers la droite les ressources critiques. Si la feuille de style principale ou l’image LCP démarrent plus tard qu’avant, un hint leur prend de la bande passante.

Une règle pour s’en souvenir

  • fetchpriority quand le navigateur trouve la ressource à temps mais ne sait pas qu’elle est importante.
  • preload quand la ressource est importante mais que le navigateur la trouverait tard.
  • preconnect quand la ressource vient d’un autre domaine et est nécessaire tôt.
  • dns-prefetch quand le domaine sera nécessaire plus tard et ne justifie pas une connexion complète.
  • prefetch ou Speculation Rules quand la ressource est destinée à la page suivante.

En résumé

Les resource hints et fetchpriority permettent de transmettre au navigateur la connaissance que seul possède celui qui a construit la page : quelle ressource compte le plus, laquelle viendra d’un autre domaine, laquelle serait découverte trop tard. Pour le LCP, la modification la plus efficace est presque toujours fetchpriority="high" sur l’image principale ; le preload est pour les polices et les ressources cachées dans le CSS ou le JavaScript ; le preconnect pour quelques domaines critiques.

Ce sont des outils puissants précisément parce qu’ils changent l’ordre dans lequel travaille le navigateur, et pour la même raison il faut les utiliser avec modération. Chaque hint superflu prend des ressources aux hints utiles. La règle pratique est simple : peu de hints, ciblés, vérifiés dans le graphique en cascade avant et après.

L’ÉTAPE SUIVANTE

Commençons
par votre site.

Parlez-nous du projet, des problèmes que vous constatez et de vos objectifs. Ensemble, nous définirons une intervention sur mesure.

Demander une analyse

Vous mène au formulaire de la page Contact.

MANAGED SERVER S.R.L.info@corewebvitals.fr +39 02 5656 9681

Du lundi au vendredi · 9 h 30 – 19 h 30 (CET)

Vous préférez un formulaire en ligne ?
Contactez-nous sur managedserver.it ↗