Debugging & Troubleshooting

Comment Netalith.com est passé de 81 à 93 sur PageSpeed Insights

Étude de cas détaillée : migration de Tailwind CDN vers tailwind.config.js, erreur de version du CLI et correction pour Django + Tailwind CSS.

Photo de profil de Long Nguyen

Long Nguyen

Développeur fullstack · Ingénieur IA · Chercheur

3 min de lecture
Score de performance mobile PageSpeed Insights passant de 81 à 93 après la suppression du CDN Tailwind

Le problème : bloqué à 81 sur PageSpeed Insights

Netalith.com obtenait un score de 81 en performance et de 96 en accessibilité dans le rapport mobile de PageSpeed Insights. Le rapport complet cité dans cet article est disponible ici : rapport PageSpeed Insights de netalith.com.

Les diagnostics du rapport signalaient une chaîne de requêtes bloquant le rendu et ajoutant environ 1 140 millisecondes de délai. Deux feuilles de style étaient chargées sur chaque page : le script CDN de Tailwind et une feuille de style compilée séparément, chargés côte à côte et contenant en grande partie le même CSS.

Chaîne de requêtes réseau montrant le chargement simultané du CDN Tailwind et de flow.css, à l’origine d’un délai bloquant le rendu

Étape 1 : créer tailwind.config.js et y migrer la configuration du CDN

Avant de supprimer quoi que ce soit, il fallait déplacer la configuration du thème dans un emplacement qui ne dépendait pas de la présence du script CDN. La configuration d’origine était intégrée directement à côté du script CDN et faisait référence à un objet global tailwind, créé uniquement parce que ce script le génère :

<script src="https://cdn.tailwindcss.com"></script>

Un nouveau fichier, tailwind.config.js, a été créé à la racine du projet, à côté de manage.py. Toutes les valeurs du bloc précédent y ont été déplacées, encapsulées dans module.exports au lieu d’être affectées à l’objet global tailwind. Une propriété content a également été ajoutée pour indiquer au CLI où se trouvent les vrais fichiers de templates, ce dont le build CDN n’avait pas besoin puisqu’il compilait tout directement dans le navigateur :

// tailwind.config.js 
module.exports = {
  content: ["./templates/**/*.html"],
  darkMode: 'class',
  theme: {
        extend: {
            colors: {
                primary: {
                    "50": "#eff6ff",
                    "100": "#dbeafe",
                    "200": "#bfdbfe",
                    "300": "#93c5fd",
                    "400": "#60a5fa",
                    "500": "#3b82f6",
                    "600": "#2563eb",
                    "700": "#1d4ed8",
                    "800": "#1e40af",
                    "900": "#1e3a8a",
                    "950": "#172554"
                },
                brand: {
                    DEFAULT: '#2563eb',
                    dark: '#1d4ed8',
                },
            }
        },
        fontFamily: {
            'body': [
                'Inter', 'ui-sans-serif', 'system-ui', '-apple-system', 'system-ui',
                'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans',
                'sans-serif', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'
            ],
            'sans': [
                'Inter', 'ui-sans-serif', 'system-ui', '-apple-system', 'system-ui',
                'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans',
                'sans-serif', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'
            ]
        }
    }
}

Un détail structurel est important ici : colors doit rester dans theme.extend, et non directement dans theme. Placer les couleurs personnalisées directement sous theme remplace toute la palette par défaut de Tailwind au lieu de la compléter, ce qui supprime silencieusement du build toutes les classes par défaut comme bg-blue-600. fontFamily reste au même niveau que dans la configuration CDN d’origine, conformément aux dépendances existantes du site.

Ensuite, un fichier CSS source a été créé pour servir de point de départ à la compilation du CLI, en suivant la structure de fichiers statiques déjà utilisée par le projet (STATICFILES_DIRS pointait vers un dossier public/) :

@tailwind base;
@tailwind components;
@tailwind utilities;

Le build a ensuite été configuré pour générer le fichier public/dist/output.css, correspondant au chemin chargé par le template Django avec {% static 'dist/output.css' %}.

Étape 2 : supprimer le script CDN

Une fois la configuration migrée, la balise du script CDN et son bloc de configuration intégré ont été entièrement supprimés du template de base. Ils ont été remplacés par un unique lien vers la feuille de style que le CLI allait générer :

<link rel=\"stylesheet\" href=\"{% static 'dist/output.css' %}\">

À ce stade, rien n’avait encore été compilé. C’est à cette étape qu’une nouvelle erreur s’est produite.

Étape 3 : l’erreur — télécharger le dernier CLI sans vérifier la version du CDN

Pour générer le véritable fichier CSS, il fallait installer le CLI Tailwind. Tailwind propose un exécutable autonome pour chaque plateforme sur sa page GitHub des releases ; sous Windows, aucune installation de Node.js n’est donc nécessaire : github.com/tailwindlabs/tailwindcss/releases.

L’erreur a consisté à télécharger l’exécutable situé en haut de cette page, qui correspond à la dernière version disponible, sans vérifier d’abord quelle version majeure était réellement utilisée par le site. Le script CDN utilisé jusque-là, https://cdn.tailwindcss.com, fournit Tailwind CSS v3. Or, l’exécutable CLI téléchargé depuis GitHub correspondait à l’époque à Tailwind v4. Deux versions majeures différentes du même outil, avec des mécanismes internes différents pour lire la configuration et rechercher les classes, ont ainsi été associées sans que cette incompatibilité soit vérifiée.

Étape 4 : compiler, déployer… et casser la mise en page

L’exécutable v4 a lancé la compilation sans afficher la moindre erreur et a produit un fichier output.css. En local, la plus grande partie de la page semblait correcte, la modification a donc été déployée.

Sur le site en production, une section hero utilisant une grille personnalisée, lg:grid-cols-[1.2fr_0.8fr], est passée de deux colonnes à une seule, tandis que l’image mise en avant située à côté a disparu. Aucune erreur n’apparaissait dans la console du navigateur et aucune requête n’échouait. Le fichier compilé output.css ne contenait tout simplement aucune règle CSS pour cette classe de grille.

Voilà à quoi ressemble concrètement une incompatibilité v3/v4 : le CLI v4 n’interprétait pas le tailwind.config.js au format v3 de la même façon que v3 lui-même. Une partie de la détection des classes ne fonctionnait donc pas correctement et certaines classes utilisées dans les templates étaient silencieusement absentes du fichier final. Le build n’a jamais échoué de manière explicite : il a simplement livré une feuille de style incomplète.

 

Mise en page cassée en production sur Netalith : la section hero à deux colonnes est réduite à une colonne et l’image mise en avant a disparu

 

Étape 5 : télécharger le bon build v3 et recompiler

La solution a consisté à revenir sur la page des releases GitHub et à télécharger une version v3 précise plutôt que le lien par défaut vers la dernière version, par exemple v3.4.19. Cette version majeure correspondait à celle fournie par le script CDN et au format utilisé par le fichier tailwind.config.js existant. Dans la liste Assets de cette release, le bon fichier pour une machine Windows standard 64 bits est tailwindcss-windows-x64.exe. Il a été renommé en tailwindcss.exe et placé à la racine du projet, en remplacement de l’exécutable v4.

La recompilation avec la même commande et le même fichier tailwind.config.js, cette fois avec un exécutable correspondant à la bonne version, a produit un fichier de sortie sensiblement différent. La classe de grille qui manquait précédemment apparaissait bien dans le CSS compilé. Après redéploiement, la mise en page correspondait à nouveau à la version rendue par le CDN.

Le processus complet, réalisé correctement

Voici la séquence qui fonctionne réellement, sans l’erreur de version au milieu :

  1. Emplacements des fichiers statiques. Le fichier source se trouve dans public/src/input.css et le fichier compilé dans public/dist/output.css. Cette organisation correspond à la structure STATICFILES_DIRS existante du projet, sans nécessiter de modification de la gestion des fichiers statiques par Django.
  2. Créer tailwind.config.js. Déplacer chaque valeur de l’objet tailwind.config intégré au CDN dans module.exports, conserver les couleurs personnalisées dans theme.extend et ajouter une propriété content pointant vers ./templates/**/*.html, ou vers l’emplacement réel des templates du projet.
  3. Supprimer le script CDN et son bloc de configuration intégré du template de base, puis les remplacer par un lien vers la feuille de style compilée.
  4. Vérifier la version du CDN avant de télécharger le CLI. L’URL et le comportement du script CDN indiquent la version majeure utilisée ; l’exécutable CLI téléchargé doit lui correspondre, et ne doit pas être choisi simplement parce qu’il s’agit de la dernière release affichée sur GitHub.
  5. Télécharger l’exécutable correspondant à la bonne version depuis la page GitHub de la release appropriée ; sous Windows, il s’agit de l’élément tailwindcss-windows-x64.exe, à renommer en tailwindcss.exe.
  6. Compiler : .\tailwindcss.exe -i public/src/input.css -o public/dist/output.css --minify, ou utiliser --watch pendant le développement actif.
  7. Vérifier avant de déployer. Comparer la page rendue avec l’ancienne version basée sur le CDN dans plusieurs templates et vérifier que la taille du fichier de sortie correspond à un build complet, et non à un build partiel.

Les résultats

Après la suppression complète du CDN et le déploiement de la feuille de style correctement compilée, ainsi que l’ajout de fetchpriority=\"high\" à l’image associée au plus grand élément de contenu affiché et la correction de deux éléments d’interface présentant un contraste insuffisant dans le même rapport, les résultats mobiles de PageSpeed Insights étaient les suivants :

  • Performance : 81 → 93
  • Accessibilité : 96 → 100
  • Bonnes pratiques : 100
  • SEO : 100
Résultats finaux de PageSpeed Insights : 93 en performance, 100 en accessibilité, 100 en bonnes pratiques et 100 en SEO

Le Largest Contentful Paint (LCP) s’est amélioré, mais reste le principal axe d’optimisation. Il dépend désormais surtout des dimensions des images plutôt que des ressources bloquant le rendu ; ce sera l’étape suivante de l’optimisation.

Les enseignements à retenir

  • Lorsqu’un outil basé sur un CDN est remplacé par une version compilée localement, il faut vérifier la version majeure réellement fournie par le CDN avant de télécharger quoi que ce soit. Le téléchargement par défaut de la dernière version n’est pas automatiquement le bon choix.
  • Un build Tailwind qui se termine sans erreur ne garantit pas que le résultat est correct. Les classes manquantes ne génèrent aucun avertissement : elles provoquent seulement une mise en page cassée après le déploiement.
  • Les couleurs personnalisées doivent se trouver dans theme.extend, et non directement dans theme, sans quoi toute la palette par défaut est silencieusement remplacée.
  • Vérifier la feuille de style recompilée sur plusieurs pages avant le déploiement permet de détecter les incompatibilités de version et de chemin de contenu qu’un simple contrôle de la page d’accueil ne révélerait pas.

FAQ

Questions fréquentes

Pourquoi le CDN Tailwind dégrade-t-il les scores PageSpeed Insights ?

Le script CDN Tailwind (cdn.tailwindcss.com) envoie l’ensemble du framework au navigateur et compile les classes CSS à l’exécution avec JavaScript. Cela ajoute un poids important qui bloque le chemin critique du rendu et inclut des classes utilitaires que la page n’utilise jamais, ce qui fait directement baisser le score de performance dans Lighthouse et PageSpeed Insights.

Qu’est-ce qui remplace le script CDN Tailwind en production ?

Un fichier CSS statique compilé à l’avance avec le CLI Tailwind. Le CLI analyse les templates, génère uniquement les classes CSS réellement utilisées dans le projet et produit une seule feuille de style minifiée, chargée immédiatement sans compilation à l’exécution.

Est-il risqué de passer du CDN Tailwind à un build compilé sur un site en production ?

Oui, si la migration est effectuée directement en production sans tests préalables. Une configuration à l’exécution qui dépend de l’objet global <code>tailwind</code> utilisé par le script CDN provoquera des erreurs après sa suppression. De même, toute classe CSS que l’analyse du contenu ne détecte pas sera absente de la feuille de style finale, ce qui peut casser visiblement la mise en page.

Quels autres problèmes ont affecté le score PageSpeed en dehors du CDN Tailwind ?

Des images trop lourdes, servies dans des dimensions supérieures à leur taille d’affichage, l’absence de <code>fetchpriority</code> sur l’image associée au plus grand élément de contenu affiché et des durées de mise en cache trop courtes pour des ressources statiques qui utilisaient déjà des noms de fichiers hachés. La correction du problème lié au CDN a eu le plus grand impact, mais les dimensions des images et la mise en cache restent des pistes d’amélioration.

Le passage à un build Tailwind compilé affecte-t-il l’accessibilité ou les scores SEO ?

Pas directement, mais cette même phase d’optimisation est l’occasion idéale de corriger aussi les problèmes d’accessibilité, comme un contraste de couleurs insuffisant, puisque les deux types de problèmes sont signalés dans le même rapport PageSpeed Insights. Dans ce cas, la correction de deux éléments présentant un faible contraste, en parallèle de la modification du CSS, a permis d’atteindre un score d’accessibilité parfait de 100.

Restez informé avec Netalith

Recevez des ressources de développement, des mises à jour produit et des offres spéciales directement dans votre boîte mail.