Comment personnaliser l'identité visuelle de GLPI avec le module Branding

Un branding GLPI qui survit aux mises à jour : pourquoi modifier le cœur efface la marque à la mise à jour, le DOM réel de l'écran de connexion dans GLPI 11 (span.glpi-logo, body.welcome-anonymous), le CSS que le module injecte par hook, une requête SQL d'audit et les pièges de terrain.

Changer le logo de GLPI ressemble à un réglage de cinq minutes - jusqu'à ce que la prochaine mise à jour efface tout. Personnaliser l'identité visuelle en modifiant le CSS du cœur ou le template de connexion fonctionne aujourd'hui, mais c'est une dette qui arrive à échéance toute seule : elle disparaît à la mise à jour et, dans le passage de GLPI 10 à 11, le DOM de l'écran de connexion a été renommé, donc le vieux hack cesse de s'appliquer sans lancer la moindre erreur. Ce guide montre comment faire un branding qui survit aux mises à jour, où vit chaque pièce dans GLPI 11 et les pièges qui n'apparaissent qu'en environnement client.

Le branding dans GLPI n'est pas que de l'esthétique

Un GLPI aux couleurs de l'organisation réduit la friction : l'utilisateur final reconnaît le Service Desk de l'entreprise, et non un système étrange qui demande un identifiant. Mais le coût caché n'est pas dans le choix de la bonne couleur - il est dans la manière dont la personnalisation est appliquée. Il y a trois voies, et deux d'entre elles se paient cher plus tard.

  • Modifier le cœur (CSS, images ou templates dans glpi/css, glpi/pics ou templates/) : ça marche, mais le prochain git pull ou l'extraction de la release écrase les fichiers. La personnalisation s'évapore et personne ne se souvient pourquoi.
  • Thème/CSS injecté de l'extérieur : il survit à la mise à jour des fichiers, mais il est lié aux sélecteurs de cette version. Quand GLPI change la structure HTML - comme il l'a fait du 10 au 11 - le sélecteur n'existe plus et le CSS devient lettre morte, silencieusement.
  • Hook de plugin : GLPI expose des points d'injection officiels (display_login, add_javascript). Le module applique la marque par ces hooks, sans toucher à un seul fichier du cœur. C'est la seule voie qui survit aux mises à jour par conception.

Ce que l'on peut personnaliser - et où vit chaque pièce

Chaque élément visuel de GLPI vit à un endroit différent du DOM, et le changer sans connaître la bonne cible est ce qui génère la plupart des tickets "je l'ai changé et rien ne s'est passé". La matrice ci-dessous relie chaque pièce à sa clé de configuration et au mécanisme réel dans GLPI 11 :

PièceClé de configMécanisme dans GLPI 11Piège courant
Faviconfavicon_picture<link rel="icon"> réécrit via JSLe cache agressif du navigateur retient l'ancienne icône
Titre de l'ongletpage_titledocument.titleLes écrans qui fixent leur propre titre le rétablissent
Logo de connexionlogin_picturespan.glpi-logo via content: url(...)Viser .login-logo, qui n'existe pas dans le 11
Fond de connexionlogin_backgroundbody.welcome-anonymous / .page-anonymousViser body.login-page, qui n'existe pas dans le 11
Logo de l'en-têteinternal_pictureEn-tête interne via add_javascriptLe confondre avec le logo de connexion (cibles distinctes)
Pied de pagefooter_mode / footer_text.copyright (thème par défaut du 11)Coller du HTML non assaini dans le texte personnalisé
Couleurs de l'interfacecolor_*Variables CSS Tabler (--tblr-*)Passer une valeur non hex et injecter un CSS cassé

Le piège de GLPI 11 : le logo de connexion n'est pas un fond

En maintenance d'environnements clients, le ticket classique est : "nous avons mis à jour GLPI et l'écran de connexion est revenu au standard". La cause est presque toujours une personnalisation faite directement dans le cœur ou dans un CSS qui visait les sélecteurs de GLPI 10. Dans le passage au 11, le template de l'écran de connexion (page_card_notlogged.html.twig) a été réécrit : le <body> est devenu body.welcome-anonymous, le conteneur est devenu .page-anonymous et le logo a cessé d'être une image de fond - c'est désormais un <span class="glpi-logo"> rendu par content: var(--logo). Nous l'avons découvert en pratique le 2026-06-10, en cherchant pourquoi le fond de connexion d'un client ne s'appliquait tout simplement pas après la mise à niveau : l'ancien sélecteur body.login-page n'existait plus dans le DOM. Aucune erreur dans le log - le CSS était correct pour une version qui ne tournait plus. C'est pourquoi nous émettons désormais les bons sélecteurs par hook, versionnés avec le module. Voici le CSS réel que le module injecte - et les sélecteurs morts qui expliquent la disparition :

/* GLPI 11 - selecteurs REELS de l'ecran de connexion (page_card_notlogged.html.twig) */
/* Le logo N'EST PAS un fond : c'est un <span class="glpi-logo"> avec content: var(--logo) */
.page-anonymous .glpi-logo {
  content: url("/plugins/nextool/front/module_assets.php?module=branding&file=...") !important;
  width: 240px !important;
  height: 130px !important;
  object-fit: contain !important;   /* preserve le rapport d'aspect de l'image */
}

/* Le fond va sur body.welcome-anonymous / .page-anonymous - JAMAIS sur body.login-page */
body.welcome-anonymous,
.page-anonymous {
  background-image: url("...") !important;
  background-size: cover !important;
  background-position: center center !important;
}

/* Selecteurs de GLPI 10 qui N'EXISTENT PLUS dans le 11 (pourquoi le vieux hack disparait sans erreur) :
   .login-logo      -> desormais span.glpi-logo
   body.login-page  -> desormais body.welcome-anonymous  */

Comment le module s'applique sans toucher au cœur

Sur l'écran de connexion il n'y a pas de session, donc le module s'accroche au hook display_login et injecte un bloc <style>/<script> en ligne. Les images (favicon, logo, fond) sont servies par un endpoint stateless qui n'exige pas de connexion, avec trois protections qui méritent d'être nommées car c'est exactement là que des plugins négligents fuient : l'extension passe par une liste blanche (png, jpg, jpeg, gif, ico, svg, webp), le chemin est résolu avec realpath() et vérifié contre le préfixe du répertoire d'envoi (blocage de path traversal), et le fichier est enregistré avec un nom unique via uniqid() pour ne ni entrer en collision ni écraser. Sur les pages internes, déjà authentifiées, la personnalisation entre par le hook add_javascript.

Un détail de performance qui n'apparaît qu'en mesurant : le JS interne part avec Cache-Control: private, max-age=86400 et les images avec public, max-age=604800 (une semaine), mais sans ETag. La raison est concrète - avec ETag et max-age ensemble, Chrome revalidait (HTTP 304) à chaque chargement de page, et chaque 304 payait encore le bootstrap complet de GLPI sur le serveur. Un hash de la configuration entre dans le paramètre fv= de l'URL, donc enregistrer la config change l'URL et invalide le cache instantanément, sans revalidation.

Configuration en cinq étapes

  1. Activez le module Branding dans le catalogue de modules NexTool.
  2. Dans l'onglet Configuration, envoyez le favicon, le logo de connexion et le logo de l'en-tête ; définissez la largeur et la hauteur de chaque logo (accepte px, em, rem, %, vw, vh).
  3. Envoyez le fond de l'écran de connexion et ajustez le titre de l'onglet du navigateur.
  4. Choisissez le comportement du pied de page : garder l'original, le masquer ou le remplacer par du texte (HTML limité aux balises sûres).
  5. Optionnel : définissez les couleurs de l'interface par variables CSS (le module valide chaque valeur comme hex avant de l'émettre, empêchant l'injection CSS). Rechargez GLPI pour voir le résultat.

Audit : qui a changé quoi

Chaque action - activer, désactiver, enregistrer la config, envoyer ou supprimer une image - est consignée dans une table de log du module. Dans un environnement à plusieurs administrateurs, cette piste répond à "qui a changé le logo vendredi soir" sans dépendre de la mémoire. La requête de diagnostic :

-- Piste d'audit du module Branding : qui a change l'identite visuelle et quand
SELECT l.date_creation  AS quand,
       u.name           AS utilisateur,
       l.action         AS action,    -- save_config, upload_picture, delete_picture, toggle_enable/disable
       l.detail         AS detail
FROM   glpi_plugin_nextool_branding_log l
LEFT   JOIN glpi_users u ON u.id = l.users_id
ORDER  BY l.date_creation DESC
LIMIT  20;

Pièges de terrain

  • Favicon qui ne change pas - le navigateur met en cache le favicon de manière agressive, par onglet et par session. Après avoir envoyé la nouvelle icône, un rechargement forcé (Ctrl+Shift+R) ou fermer et rouvrir l'onglet résout le problème ; ce n'est pas un bug du module.
  • SVG de source fiable - le SVG est idéal pour un logo (il s'agrandit sans perdre en netteté), mais il peut porter un script embarqué. N'utilisez que des SVG issus d'un visuel que vous avez généré vous-même ; l'endpoint sert le fichier avec le bon Content-Type, mais l'hygiène commence à l'envoi.
  • Pied de page avec du HTML collé - le texte personnalisé du pied passe par un assainissement qui n'autorise qu'une poignée de balises (a, b, strong, i, em, span, br). Coller un bloc entier de HTML marketing ne rendra tout simplement pas ce que vous attendez.
  • Rapport d'aspect du logo - définir une largeur et une hauteur fixes sans respecter la proportion d'origine déforme l'image. Le module applique object-fit: contain justement pour ne pas étirer, mais vérifiez-le à l'écran.
  • Mise à niveau du 10 au 11 - si le client avait déjà un branding manuel sur GLPI 10, il disparaîtra dans le 11 à cause des sélecteurs changés. Traitez le branding comme une partie du plan de mise à niveau, pas comme une surprise post-migration.

Vous voulez standardiser l'identité visuelle de GLPI sur tous les environnements que vous administrez, sans rouvrir le sujet à chaque mise à jour ? Le module Branding est gratuit et applique la marque par des hooks natifs. Et si vous préférez externaliser le déploiement et la maintenance de GLPI, NexTool maintient des environnements clients avec un branding versionné et résistant aux mises à jour.


Este conteúdo foi produzido com auxílio de inteligência artificial e revisado pela equipe Nextool Solutions.

Questions fréquentes

Parce que la personnalisation a été faite directement dans le cœur (fichiers CSS, images ou templates), et la mise à jour de GLPI écrase ces fichiers. Pour survivre aux mises à jour, il faut appliquer la marque par des hooks de plugin (display_login, add_javascript), sans toucher au cœur - c'est exactement le rôle du module Branding.

Dans GLPI 11, le template de l'écran de connexion a été réécrit. Le logo n'est plus une image de fond ; il est devenu un <span class="glpi-logo"> avec content: var(--logo) ; le body est devenu body.welcome-anonymous et le conteneur .page-anonymous. Si le CSS vise les sélecteurs de GLPI 10 (.login-logo, body.login-page), ils n'existent plus dans le DOM et la règle ne s'applique pas, sans erreur lancée.

Non. Le module Branding applique favicon, logos, fond de connexion, pied de page et couleurs par l'interface, via des hooks natifs. Les changements ne vivent pas dans les fichiers du cœur, donc ils survivent aux mises à jour de GLPI.

Dans l'onglet Configuration du module, envoyez l'image du favicon et saisissez le titre voulu. Le module réécrit le <link rel="icon"> et le document.title via JavaScript. Si la nouvelle icône n'apparaît pas, c'est le cache du navigateur : un rechargement forcé (Ctrl+Shift+R) ou la réouverture de l'onglet résout le problème.

Oui, le SVG est accepté et s'agrandit sans perdre en netteté. L'envoi passe par une liste blanche d'extensions et le service protège contre le path traversal. Comme le SVG peut contenir un script embarqué, n'utilisez que des visuels de source fiable (idéalement générés par vous), pas des fichiers d'origine inconnue.

Oui. Branding est gratuit, fait partie de NexTool et est compatible avec GLPI 10 et 11. Attention à la mise à niveau : s'il y avait un branding manuel sur le 10, il ne migre pas seul vers le 11 (les sélecteurs de l'écran de connexion ont changé) ; réappliquez-le par le module après la migration.

Besoin d'aide ?