Performance : comment Drupal va plaire à Google :)

Photo illustrant une conférence interne et le débat sur la performance d'un site Drupal
L'indice de performance de Google distribue les notes, mais surtout les recommandations. Comment le satisfaire avec un site Drupal ?

Google PageSpeed ?

Google PageSpeed ​​est l'indicateur de référence pour mesurer la performance d'affichage des sites, notamment en mobilité. Google utilise des critères de performance dans ses indices de classement des sites en SEO. 

Chaque site analysé est noté de 0 à 100. L'objectif sous-tendu par Google est de disposer d'une note supérieure à 90 sur 100... Et l'objectif n'est pas si évident à atteindre, notamment pour la note mobile, lorsque l'on utilise un CMS sans optimisations particulières. Notons également que Google Page Speed analyse un parcours utilisateur anonyme.

Les éléments analysés

  • First Contentful Paint - Cette métrique est la vitesse de chargement perçue par l'utilisateur (affichage du contenu utile). 
  • Time to Interactive - Le temps d'interactivité mesure le temps qu'il faut à une page pour devenir entièrement interactive (la page répond aux interactions de l'utilisateur dans les 50 millisecondes).
  • Speed Index - L'indice de vitesse mesure la vitesse à laquelle le contenu de notre page est affiché à l’écran pendant le chargement de la page. L'indice de vitesse sera très différent selon la technologie utilisée pour générer le contenu du site. Cet indice est obtenu en comparant une grande quantité de sites analysés.
  • Total Blocking Time - Ce temps mesure les séquences tnre le First Contentfull Paint et le Time to Interactive, lorsque la durée de la tâche a dépassé 50 millisecondes.
  • Largest Contentful Paint - Cette métrique mesure la vitesse de chargement de l'élément le plus lourd sur la page (une image décorative sur toute la largeur de la page, par exemple).
  • Cumulative Layout Shift - Google mesure ici le mouvement des éléments visibles dans la fenêtre (en cas de chargement de polices tierces ou d'utilisation de JavaScript pour ajouter des styles. 

Google détecte en outre que notre site repose sur Drupal et il nous suggère des optimisations ou des modules en conséquence.

Optimisation de la performance d'un site Drupal

Il n'y a pas de magie mais une méthode, empirique. A chaque optimisation, il nous faut tester les résultats et avancer pas à pas. A ce propos, le module Site Audit aide à obtenir des rapports rapides et fiables pour détecter les problèmes courants et fournir une analyse du site Drupal. Les rapports peuvent être générés dans plusieurs formats, notamment en texte brut, HTML et JSON.

Agréger les fichiers CSS et JavaScript

Parmi les vérifications à opérer avant la mise en production du site, il est recommandé d'activer l'agrégation CSS et JavaScript. Cette opération réduit le nombre de fichiers CSS et JS. Pour l'activer, nous nous rendons sur la page /admin/config/development/performance et nous assurons que les deux options sont activées.

En réduisant simplement le nombre de requêtes HTTP brutes adressées au serveur, nous réduirons le temps nécessaire pour télécharger les actifs nécessaires à l'affichage de la page. 

Installer le module AdvAgg

L'agrégation CSS et JavaScript peut être améliorée par le module AdvAgg . Ce module propose de nombreuses options, notamment en minimisant et en compressant les ressources de notre site.  Google PageSpeed, de son côté, mesurera la compression des CSS et JavaScript.

Comme certains paramètres peuvent altérer l'affichage du site, il convient bien entendu de tester cette activation dans l'environnement de développement au préalable. Elle nécessite souvent quelques ajustements et contournements dans le code.

Réduire le HTML

Il est conseillé, en plus du CSS et du JS, de réduire également le poids de l'html, en supprimant les commentaires et autres sauts de ligne du code. Cette opération peut être facilitée en utilisant le module Minify Source HTML. Il s'agit d'un module simple qui réduit la page au strict minimum. Si les gains de performance ne sont pas drastiques, il s'agit d'un détail de plus à ajouter à notre liste d'optimisations.

Réduire le nombre d'éléments DOM

L'objet est ici de limiter le nombre de <div> et d'imbrications sur une page, qui créent un grand nombre d'objets et augmentera l'utilisation de la mémoire nécessaire au navigateur pour les stocker. 

Google PageSpeed ​​pénalise la note de performance si le DOM du site comporte plus de 800 éléments et génère une erreur lorsqu'il dépasse 1.400 éléments. Il sanctionne également la note s'il y a plus de 32 niveaux d'éléments imbriqués.

Réduire la taille du DOM avec Drupal peut être assez difficile. Chaque entité, champ et élément est enveloppé par au moins une div, parfois plus. L'intervention d'optimisation, fastidieuse, est de supprimer toutes les balises non nécessaires. C'est donc une intervention délicate car subvective pour le développeur, qui doit évaluer l'équilibre entre le gains de performance et l'expérience des utilisateurs (notamment le nombre d'éléments dans une page). Elle concerne par conséquent autant l'équipe de conception que l'équipe technique !

Suppression des styles inutilisés

Drupal ajoute un certain nombre de fichiers CSS au thème. La meilleure solution est peut-être de les supprimer puis d'uniquement ajouter les fichiers dont nous avons besoin. La suppression des styles qui ne sont pas nécessaires à notre thème est essentielle pour améliorer la taille globale de notre page.

Pour supprimer les fichiers CSS, nous devons ajouter une directive library-override au fichier .info.yml du thème, en ajoutant la valeur "false" à chacun des fichiers que nous souhaitons supprimer.

libraries-override:
  system/base:
    css:
      component:
        css/components/ajax-progress.module.css: false
        css/components/align.module.css: false
        css/components/autocomplete-loading.module.css: false
        css/components/fieldgroup.module.css: false
        css/components/container-inline.module.css: false
        css/components/clearfix.module.css: false
        css/components/details.module.css: false
        css/components/item-list.module.css: false
        css/components/js.module.css: false
        css/components/nowrap.module.css: false
        css/components/position-container.module.css: false
        css/components/progress.module.css: false
        css/components/resize.module.css: false
        css/components/sticky-header.module.css: false
        css/components/system-status-counter.css: false
        css/components/system-status-report-counters.css: false
        css/components/system-status-report-general-info.css: false
        css/components/tabledrag.module.css: false
        css/components/tablesort.module.css: false
        css/components/tree-child.module.css: false

Après avoir vidé les cache, notre thème sera diminué des styles associés.

En ce qui concerne les fichiers JavaScript, Drupal ne charge un fichier JavaScript (et ses dépendances) que si la fonctionnalité est nécessaire. Si un module est mal configuré, il risque d'ajouter des fichiers de script à toutes les pages. Les supprimer avec hook_page_attachments_alter() aide à améliorer le score de la page.

Éliminer les ressources bloquant le rendu

Le chargement de la page peut être ralenti par des téléchargements, nécessaires à l'affichage de la page. Pendant que la page est occupée à télécharger le CSS et le JavaScript, les utilisateurs attendent. Ce temps d'attente est appelé Time to Interactive par Google PageSpeed.

Il est possible d'atténuer ce délai par quelques optimisations :

  • Éviter l'utilisation d'instructions @import dans nos fichiers CSS (requête HTTP supplémentaire pour récupérer le fichier CSS).
  • Optimiser le CSS afin de ne charger que le CSS utile (important lorsque nous utilisons des frameworks CSS comme Tailwind, car le package est volumineux et il n'est pas nécessaire de tout utiliser). 
  • Minimiser et réduire le CSS au maximum, dans un ou deux petits fichiers.
  • Différer le CSS en utilisant JavaScript pour les charger une fois après le rendu principal de la page. 

Pour éviter que JavaScript ne bloque le rendu, il est possible déplacer toutes les balises de script vers le bas de la page. Nous pouvons en outre nous assurer qu'ils ne feront pas partie du chargement initial de la page en ajoutant l'attribut defer. Les dépendances JavaScript de Drupal permettent de savoir quels fichiers doivent être chargés en premier et garantissent que les scripts soient ajoutés dans le bon ordre.

Il est également possible d'utiliser le module AdvAgg Modifier (inclus dans AdvAgg) afin de différer davantage le CSS et le JavaScript sur la page.

Redimensionner et réduire les images

L'optimisation des images est fondamental pour l'index de Google PageSpeed. En utilisant le module Media, nous devons nous assurer que Drupal redimensionnera et réduira la taille de toutes les images que nous téléchargeons grâce à des styles d'image correctement configurés.

Il convient en effet de s'assurer que le style d'image redimensionne l'image l'affichage dans lequel elle est rendue. Cela empêche l'affichage d'une image trop grande et lourde pour un utilisateur qui consulte la page en version mobile.

En plus de redimensionner l'image, nous devons également réduire la taille des fichiers image. La page sur /admin/config/media/image-toolkit NOUS permet de contrôler la qualité de compression des images jpeg. Définir cette valeur de qualité à environ 75% réduira considérablement la taille de NOTRE image, sans avoir d'effet notable sur sa qualité.

Il est conseillé de répéter l'opération avec le format d'image png. Le module TinyPNG peut être utilisé pour réduire considérablement la taille du fichier (réduction de 20 à 30% du poids). Le module TinyPNG a besoin d'une clé API, mais le service reste gratuit pour 500 images, ce qui est suffisant pour la plupart de nos projets.

Utiliser les formats d'image nouvelle génération

Google PageSpeed ​​nous demande souvent de "Servir des images dans des formats de nouvelle génération". Cette recommandation est centrée sur l'utilisation d'images au format WebP et AVIF car elles disposent d'une meilleure compression que les autres formats (y compris png).

La prise en charge de WebP par les navigateurs ne s'est généralisée que très récemment. Nous activons désormais, pour tous nos projets, l'option disponible dans le Core de Drupal.

Définir la hauteur et la largeur de l'image

Si nous ne définissons pas une hauteur et une largeur sur les images, le navigateur ne connaîtra pas la taille à afficher jusqu'à ce que le fichier soit téléchargé et placé dans la page. Cela peut entraîner des changements de mise en page, ce qui affecte la note de performance. En ajoutant la hauteur et la largeur au balisage de l'image, nous permettons au navigateur d'allouer de l'espace.

<img src="/sites/default/files/styles/some_style/public/image/une-image.png" width="250" height="250" alt="Some image" loading="lazy" typeof="foaf:Image" />

Chargement différé des images

Le chargement différé est une technique qui permet aux utilisateurs de télécharger uniquement les éléments de la page lorsqu'ils en ont besoin. Cela signifie qu'une image qui n'est pas visible directement par l'utilisateur n'est pas téléchargée tant que l'utilisateur n'a pas fait défiler l'écran. Cette technique a un impact évident sur la taille de chargement initiale de la page puisque tous les éléments ne sont pas téléchargés lors du chargement initial de la page.

Le module Lazy Load permet permet de configurer ce chargement différé. Il convient de l'installer dès le début du développement du site pour ne pas avoir à revenir, par la suite, sur les styles d'images. 

Le cache de Drupal

C'est la première chose à vérifier. Nous devons vérifier que la configuration du cache de Drupal est correcte. Il faut noter que Drupal est assez malin pour invalider lui-même les pages du cache que le contributeur vient de modifier. Il n'est donc pas nécessaire de vider le cache Drupal à chaque modification de contenu.

En-têtes de cache Drupal

Il convient ensuite de vérifier les en-têtes "x-drupal-cache" et "x-drupal-dynamic-cache". Elles indiquent l'état actuel du cache de la page avec les valeurs "hit", "miss" ou "uncachable". La configuration doit être :

  • x-drupal-cache: HIT
  • x-drupal-dynamic-cache: HIT

Isoler les problèmes avec le cache peut être assez chronophage. Le paramètre http.response.debug_cacheability_headers, dans le fichier services.yml, peut nous y aider. Pour l'activer, configurez "true" et videz les caches Drupal.

Après cette manipulation, les en-têtes "X-Drupal-Cache-Tags", "X-Drupal-Cache-Contexts" et "X-Drupal-Cache-Max-Age" sont ajoutés à la réponse. Ces en-têtes nous indiquent quels éléments ont interagi avec le cache pour produire la réponse.

Le module Devel contient un module appelé Web Profiler qui peut être utilisé pour inspecter le comportement d'une page et les éléments qui peuvent provoquer l'invalidation du cache.

Augmenter l'expiration de la mise en cache statique

Un élément actif sera servi en dehors des fichiers dynamiques gérés par Drupal (images, CSS, JavaScript ou fichier téléchargé). Par défaut, Drupal ajoute les considère séparément et leur permet d'être mis en cache pendant 2 semaines. Cette configuration peut être considérée comme insuffisante, surtout si ces ressources ne changent pas.

Ce paramètre est défini dans le fichier .htaccess.

  # Cache all files for 2 weeks after access (A).
  ExpiresDefault A1209600

Google PageSpeed ​​réduira notre score si nous avons un délai d'expiration du cache de moins d'un an. L'idéal est par conséquent de le définir comme suit : 

  # Cache all files for 2 weeks after access (A).
  ExpiresDefault A31536000

Si le projet utilise un CDN pour gérer le contenu, il est probable que la valeur du cache soit remplacée par une autre valeur. Il convient alors de vérifier sa configuration pour augmenter cette valeur à au moins un an... Puis de vérifier les en-têtes des ressources statiques et en recherchant l'en-tête "Cache-Control". Il devrait avoir une valeur de 31536000, ce qui correspond à peu près au nombre de secondes dans une année.

cache-control: max-age=31536000

Cache externe

En dernier recours et en fonction du périmètre fonctionnel du site (trafic anonyme ou pas), des solutions de cache externe peuvent considérablement soulager l'expérience de vos utilisateurs. Nous utilisons régulièrement :

  • Redis (Caching au niveau des requettes de la base) - Il allège et diminue les accès sur la base de données ;
  • Varnish (HTTP caching) - Reconnu pour améliorer beaucoup la vitesse d'affichage du site, pour le trafic anonyme... Jusqu'à son expiration.

Ceci dit, nous devons garder à l'esprit que ce type de cache fait office de "pansement" et qu'il peut améliorer la situation quand les optimisations applicatives sont déjà réalisées.

N'oublions pas le serveur !

Avant de nous lancer dans les optimisations de Drupal, n'oublions pas de solliciter notre infogérance d'hébergement pour faciliter nos travaux. Nous devons travailler sur des environnements : 

  • Correctement dimensionné (SSD / capacticé de mémoire (RAM) / Vitesse de CPU, etc ...)
  • Configurations à jour (OS, nginx, PHP, mariaDB et Vhost).
  • Compression des assets en .gzip.

Conclusion

Une situation idéale n'existe pas. Si le critère de performance est le plus important pour votre client, prévenez l'équipe de conception ! L'optimisation de le performance s'organise dès la réunion de lancement, lors des premiers ateliers UX et lors du déploiement du Design System. Il n'y a pas de magie !!

En ce qui concerne le plan d'action, nous préférons commencer par les optimisations applicatives classiques, en mesurant sans cesse leurs effets. La prise de mesure doit en outre être répétée plusieurs fois car les rapports varient d'un moment à l'autre. Google PageSpeed nous est utile, mais moins par sa note finale que par ses recommandations. Il s'agit d'un outil qui permet de concentrer nos efforts sur les points d'amélioration ciblés. Il nous aide aussi à entretenir et mettre à jour notre pile de développement.

Courage, c'est un métier difficile :)