Technique

mu-plugin

mu-plugin (Must-Use Plugin) : plugin WordPress spécial stocké dans le répertoire mu-plugins, chargé automatiquement à chaque requête, impossible à désactiver via le dashboard — outil privilégié pour le code d'infrastructure qui ne doit jamais être coupé.

Un mu-plugin (pour Must-Use Plugin) est un type spécial de plugin WordPress, stocké dans le répertoire wp-content/mu-plugins/, qui est chargé automatiquement à chaque requête, n'apparaît pas dans l'interface Plugins du dashboard et ne peut donc pas être désactivé par un administrateur. Cette particularité en fait l'outil privilégié pour le code d'infrastructure qui doit toujours être actif : détection d'environnement, configuration de cache, intégration avec un PaaS, règles de sécurité bas niveau.

Définition technique

Comment WordPress charge les mu-plugins

Au démarrage de chaque requête, WordPress parcourt le répertoire mu-plugins/ et inclut automatiquement tous les fichiers PHP présents à la racine de ce répertoire — sans récursion. Un fichier posé dans mu-plugins/monplugin.php est chargé. Un fichier posé dans mu-plugins/monplugin/index.php est ignoré, sauf si un fichier loader à la racine l'inclut explicitement. Cette contrainte simplifie le système : il n'y a pas d'activation à gérer, pas de base de données à consulter pour savoir quels plugins sont actifs — tous les fichiers de la racine sont actifs, point.

Le chargement se fait très tôt dans le cycle de vie d'une requête WordPress, avant les plugins classiques et avant le thème. Certains hooks comme muplugins_loaded permettent de s'accrocher à l'exécution au moment où tous les mu-plugins sont chargés mais avant que les plugins normaux le soient.

Différences avec les plugins classiques

  • Emplacement : mu-plugins/ au lieu de plugins/.
  • Activation : automatique et permanente. Aucun lien symbolique avec la table wp_options.
  • Interface : pas visible dans l'admin. Seule une entrée d'information apparaît dans la liste des plugins pour indiquer qu'un mu-plugin est chargé, mais aucun bouton d'action.
  • Hooks d'installation : inexistants. Un mu-plugin ne peut pas déclarer de fonction register_activation_hook() puisqu'il n'a pas de cycle d'activation.
  • Mise à jour automatique : inexistante. Il faut mettre à jour le fichier manuellement (ou via déploiement Git).

Enjeux actuels

Le rôle dans les architectures infrastructure-as-code

Dans un projet WordPress déployé sur un PaaS (Upsun, Fly.io, Platform.sh) via Bedrock, les mu-plugins deviennent le point d'intégration entre le code applicatif WordPress et l'environnement d'exécution. C'est là qu'on place :

  • La détection des variables d'environnement du PaaS (PLATFORM_RELATIONSHIPS, PLATFORM_ROUTES) pour configurer Redis, la base de données et les URL publiques dynamiquement.
  • La gestion HTTPS derrière un proxy (lecture des headers X-Forwarded-Proto).
  • Les règles de sécurité périmétrique qui doivent s'exécuter avant le chargement complet de WordPress.
  • Les endpoints spécialisés qui ne doivent pas charger WordPress (health-check, monitoring).

Tout ce code doit être versionné dans Git, reviewé en pull request, et garanti actif en production — ce qui correspond exactement à la promesse des mu-plugins.

Le piège des mu-plugins comme cheval de Troie

L'avantage "impossible à désactiver" a un revers : si un mu-plugin bug ou introduit une vulnérabilité, il n'y a pas de bouton d'urgence dans le dashboard pour le couper. La seule solution en cas d'incident est de supprimer le fichier via FTP, SSH ou un nouveau déploiement. Pour cette raison, le code d'un mu-plugin doit être audité plus rigoureusement que celui d'un plugin normal, et testé en staging avant tout déploiement en production.

Applications pratiques

Dans mon expérimentation de sécurisation de WordPress via Infrastructure as Code, les mu-plugins portent toute la logique qui doit rester en PHP mais qui ne doit jamais être coupée : un mu-plugin upsun-security.php qui implémente un nano-WAF (filtrage des méthodes HTTP, allowlist/blocklist d'User-Agents, CSP dynamique), et un mu-plugin health-check.php qui répond sur /health sans charger WordPress. Cette séparation permet de garder zéro plugin applicatif dans le dashboard tout en conservant la logique indispensable en PHP.

Questions fréquentes

Qu'est-ce qui différencie un mu-plugin d'un plugin classique ?

Trois différences fondamentales : (1) un mu-plugin vit dans wp-content/mu-plugins/ (ou web/app/mu-plugins/ en structure Bedrock) au lieu de wp-content/plugins/, (2) il est chargé automatiquement à chaque requête sans activation manuelle, et (3) il n'apparaît pas dans l'interface Plugins du dashboard WordPress — donc impossible à désactiver depuis l'admin. Un fichier PHP posé dans mu-plugins/ devient immédiatement actif sur tout le site au prochain rechargement.

Pourquoi utiliser un mu-plugin plutôt qu'un plugin normal ou du code dans functions.php ?

Un mu-plugin est le bon choix pour du code d'infrastructure qui ne doit jamais être coupé, même par erreur : configuration de cache Redis, détection HTTPS derrière un proxy, filtrage de sécurité bas niveau, intégration avec un PaaS. Contrairement à functions.php, un mu-plugin est indépendant du thème (il survit à un changement de thème). Contrairement à un plugin classique, il ne peut pas être désactivé par un administrateur qui se tromperait de case. Et contrairement au core, il est versionné dans le dépôt Git du projet — donc reviewable en pull request.

Quelles sont les limites des mu-plugins ?

Les mu-plugins ont trois limitations par rapport aux plugins classiques : (1) pas de chargement récursif — seuls les fichiers PHP à la racine de mu-plugins/ sont chargés automatiquement, les sous-répertoires sont ignorés, (2) pas de hooks d'activation/désactivation — puisqu'un mu-plugin ne s'active ni ne se désactive jamais, (3) pas de mise à jour via le dashboard — la maintenance se fait exclusivement par fichier, ce qui est un avantage pour la reproductibilité mais peut poser problème si on dépend d'un écosystème qui se met à jour fréquemment.

Ressources et documentation