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/picsoutemplates/) : ça marche, mais le prochaingit pullou 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èce | Clé de config | Mécanisme dans GLPI 11 | Piège courant |
|---|---|---|---|
| Favicon | favicon_picture | <link rel="icon"> réécrit via JS | Le cache agressif du navigateur retient l'ancienne icône |
| Titre de l'onglet | page_title | document.title | Les écrans qui fixent leur propre titre le rétablissent |
| Logo de connexion | login_picture | span.glpi-logo via content: url(...) | Viser .login-logo, qui n'existe pas dans le 11 |
| Fond de connexion | login_background | body.welcome-anonymous / .page-anonymous | Viser body.login-page, qui n'existe pas dans le 11 |
| Logo de l'en-tête | internal_picture | En-tête interne via add_javascript | Le confondre avec le logo de connexion (cibles distinctes) |
| Pied de page | footer_mode / footer_text | .copyright (thème par défaut du 11) | Coller du HTML non assaini dans le texte personnalisé |
| Couleurs de l'interface | color_* | 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
- Activez le module Branding dans le catalogue de modules NexTool.
- 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). - Envoyez le fond de l'écran de connexion et ajustez le titre de l'onglet du navigateur.
- Choisissez le comportement du pied de page : garder l'original, le masquer ou le remplacer par du texte (HTML limité aux balises sûres).
- 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: containjustement 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.