Il le peut. Et pourtant l’assistant avait raison. Nous l’avons essayé sur une copie du site, sans rien enregistrer. Des 2123 caractères de CSS additionnel, il en restait 0.
Ce qui se joue. Ce site fait partie d’un multisite : un seul WordPress qui contient plusieurs sites. Il y existe deux sortes d’administrateurs. L’administrateur du réseau peut tout faire. Un simple administrateur de site peut gérer son propre site, mais pas écrire du code libre. Et WordPress range le CSS parmi le code libre.
Le compte de mon assistant est volontairement un simple administrateur de site. Je suis moi-même administrateur du réseau, et c’est pourquoi je ne l’avais jamais remarqué.
Là où ça se gâte. Dans un thème basé sur des blocs, tout ce que vous réglez sous Styles est rangé dans un seul paquet : la palette de couleurs, les polices et aussi le CSS additionnel. Quand un simple administrateur de site enregistre ce paquet, WordPress en retire tout ce qu’il n’aurait pas eu le droit d’écrire lui-même. Donc le CSS additionnel, même s’il n’y a pas touché et même s’il était là depuis des mois.
Aucun message n’apparaît. La couleur est ajoutée, la page est enregistrée, et l’en-tête transparent, l’effet du menu et le reste du travail sur mesure ont disparu.
L’essai. Sur la copie, j’ai fait passer le même paquet deux fois par le filtre d’enregistrement de WordPress, sans vraiment l’enregistrer :
- comme simple administrateur de site : CSS additionnel de 2123 à 0 caractères, palette inchangée
- comme administrateur du réseau : CSS additionnel de 2123 à 2123 caractères, palette inchangée
Est-ce une erreur ? Non, c’est voulu. WordPress ne peut pas voir qui a écrit le CSS, et sur un multisite il ne fait confiance qu’à l’administrateur du réseau. Je ne suis pas le seul à m’y être heurté, et WordPress a rejeté la demande de changer cela :
- Ticket WordPress #58610 : la demande d’autoriser le CSS aux administrateurs de site sur un multisite. Fermé : ne sera pas corrigé (wontfix)
- Ticket WordPress #59123 : le signalement que le panneau CSS additionnel manque pour les administrateurs sur un multisite. Fermé : pas une erreur (invalid)
- Gutenberg #47062 (2023) : la modification qui épargne le CSS pour ceux qui ont bien le droit, et donc pas pour les autres
- Gutenberg #76650 (2026) : le même comportement pour le CSS sur des blocs individuels
- Multisite Custom CSS : une extension qui donne ce droit aux administrateurs de site
La solution tient en quelques lignes dans ma propre extension : celui qui peut personnaliser le thème d’un site peut aussi modifier le CSS. Ensuite le même essai, cette fois avec un vrai enregistrement : 2123 caractères avant, 2123 caractères après. C’est seulement alors que l’assistant a ajouté la couleur, sur les trois versions linguistiques.
C’est tout de même un choix délibéré. Chaque administrateur d’un site de mon réseau peut maintenant écrire du CSS libre. Dans mon réseau, je les connais tous. Dans un réseau avec des administrateurs que vous ne connaissez pas, je ne le ferais pas.
Au passage : un site qui est tombé en panne
Pour que la solution s’applique partout, j’ai activé l’extension pour tout le réseau. L’un des autres sites a aussitôt affiché une erreur. Ce site tourne sur un thème plus ancien, et ce thème contient une fonction qui porte exactement le même nom qu’une fonction de mon extension. Deux fois le même nom, ce n’est pas permis.
Le plus sournois : la page d’accueil de ce site fonctionnait normalement. Elle venait du cache. Seul celui qui voulait se connecter voyait l’erreur. J’ai donné aux fonctions de l’extension un préfixe propre, et ensuite elle a pu être activée sur tout le réseau.
Petit supplément : la couleur qui n’était pas une couleur de la palette
La nouvelle couleur était destinée à un bouton. J’avais déjà rendu ce bouton bleu auparavant, avec un code couleur isolé. Une fois la couleur dans la palette, l’éditeur l’a sagement affichée comme sélectionnée pour le bouton. Terminé, pensais-je.
Eh bien non. L’éditeur compare seulement le code couleur. S’il est par hasard identique à une couleur de la palette, c’est cette couleur de la palette qui reçoit la coche. Ce qui était enregistré, c’était toujours le code isolé. On ne remarque la différence que lorsqu’on modifie plus tard la couleur de la palette : le bouton ne suit pas.
On ne le voit que dans le code du bloc. C’est là aussi que l’assistant l’a converti.
Ce que j’en retiens
« Un administrateur peut tout de même faire ça » était vrai, et pourtant ce n’était pas une raison de continuer. L’action était permise. C’est l’effet secondaire qui posait problème.
Si vous laissez quelqu’un qui a moins de droits travailler dans l’éditeur de site, un collègue, un client ou un assistant, essayez sur une copie ce qui se passe à l’enregistrement. Non pas si cela réussit, mais ce qu’il reste ensuite.
Et pour savoir si un site fonctionne, on ne mesure pas sur la page d’accueil.
Pour les techniciens : ce qui se passe exactement
Environnement : WordPress 7.1.2 multisite (sous-domaines), thème Ollie, un utilisateur avec le rôle Administrateur sur le site, qui n’est pas super-administrateur.
1. La capacité. Dans WordPress, edit_css est liée à unfiltered_html. Dans map_meta_cap() (wp-includes/capabilities.php) :
case 'edit_css':
case 'unfiltered_html':
if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) {
$caps[] = 'do_not_allow';
} elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
} else {
$caps[] = 'unfiltered_html';
}
2. Le filtre. Pour les utilisateurs sans unfiltered_html, WordPress accroche wp_filter_global_styles_post() à content_save_pre lors de l’enregistrement. Cette fonction appelle WP_Theme_JSON::remove_insecure_properties(), où l’on trouve :
// The global styles custom CSS is not sanitized, but can only be edited by users with 'edit_css' capability.
if ( isset( $input['css'] ) && current_user_can( 'edit_css' ) ) {
$output = $input;
} else {
$output = static::remove_insecure_styles( $input );
}
Sans la capacité, styles.css est donc supprimé. Peu importe que la requête ait contenu le CSS ou non : le filtre parcourt tout le contenu de l’article de type wp_global_styles.
3. Mesurer sans écrire. Demandez les styles globaux par l’API REST avec context=edit et regardez dans _links. Si wp:action-edit-css n’y figure pas, cet utilisateur n’a pas la capacité et ne doit pas enregistrer.
4. L’essai. Avec WP-CLI, wp_set_current_user() pour chaque utilisateur, puis :
$post = get_post( $id_van_de_global_styles );
$na = json_decode( wp_unslash( wp_filter_global_styles_post( wp_slash( $post->post_content ) ) ), true );
echo strlen( $na['styles']['css'] ?? '' );
5. La solution (dans une extension à moi) :
function webtaurus_sitebeheerder_mag_css( $caps, $cap ) {
if ( 'edit_css' !== $cap || ! is_multisite() ) {
return $caps;
}
if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) {
return $caps;
}
return array( 'edit_theme_options' );
}
add_filter( 'map_meta_cap', 'webtaurus_sitebeheerder_mag_css', 10, 2 );
Cela ne touche que edit_css. Le HTML non filtré reste réservé au super-administrateur ; je l’ai vérifié après la modification.
L’avertissement qui existe bel et bien. Pour le CSS sur des blocs individuels, l’éditeur de blocs affiche un avertissement : « If you save this post, the custom CSS will be removed. » Je n’ai pas vu si l’éditeur de site avertit aussi sous Styles : l’assistant travaille par l’API REST et ne voit pas d’écran.
La collision. Cannot redeclare unregister_default_wp_widgets() : le même nom de fonction dans le functions.php du thème et dans l’extension. Une extension activée site par site, sur des sites qui ont un autre thème, ne s’en aperçoit pas. Sur tout le réseau, si. Mesurez ce genre de chose sur /wp-json/, pas sur la page d’accueil, si un cache de pages tourne.
La couleur. Ce qui était enregistré : "style":{"color":{"background":"#084b77"}}. Une couleur de la palette ressemble à ceci : "backgroundColor":"bg-blauw", avec la classe has-bg-blauw-background-color sur le bouton au lieu d’un background-color dans l’attribut style.



