Si vous avez déjà vu un plugin “marcher sur votre machine” puis casser après une mise à jour mineure de WordPress, vous connaissez le vrai sujet : les changements silencieux de comportement, plus que les nouvelles fonctionnalités visibles.
WordPress 6.9.x (et plus précisément 6.9.4, stable en avril 2026) continue une tendance nette côté core : plus de cohérence API, plus de durcissement sécurité, et des évolutions du bloc editor (Gutenberg) qui finissent par toucher vos thèmes, vos shortcodes historiques et vos intégrations page builders.
Ce qui change
Le changement “qui compte” pour les développeurs en 6.9.x n’est pas une seule API magique. C’est un ensemble de points qui, combinés, modifient votre manière de livrer des fonctionnalités : meilleure standardisation des assets (scripts/styles), durcissement des entrées/sorties (sanitization/escaping), et alignement progressif des thèmes classiques avec les contraintes de l’édition de site (FSE).
Concrètement, j’ai vu trois zones casser en production sur des sites 6.9.x : des scripts mal déclarés (mauvais chargement, dépendances manquantes), des endpoints REST trop permissifs (capabilities/nonce), et des “snippets” hérités de vieux tutos qui utilisent des hooks au mauvais moment.
- Assets (JS/CSS) : les attentes sur l’enqueue (dépendances, stratégie de chargement, versions) sont plus strictes. Les “ça marche sans dépendances” deviennent des bugs intermittents, surtout avec cache/concat/minification.
- REST API / sécurité : plus d’attention sur permission_callback, nonces, et contrôles de capacités. Les sites avec éditeurs multiples (auteurs, contributeurs) sont les premiers à se faire surprendre.
- Thèmes et blocks : l’écosystème pousse davantage vers des patterns compatibles FSE. Les thèmes classiques restent supportés, mais les intégrations “à l’ancienne” (options de thème, shortcodes de layout) demandent un plan.
Pour suivre les changements de comportement, vos meilleures sources sont les dev notes et tickets du core :
- Developer Blog (dev notes officielles)
- WordPress Core Trac (tickets)
- Repo GitHub wordpress-develop (PR et commits)
Note de méthode : je ne vais pas prétendre qu’une sous-version “.4” introduit un gros breaking change. En pratique, les casses viennent plutôt de l’accumulation (6.7 → 6.8 → 6.9), et d’un plugin/thème qui traîne des hypothèses obsolètes. L’intérêt d’une analyse “6.9.4” est de vous donner un état de l’art exploitable maintenant, avec PHP 8.1+.
Résumé rapide
- Votre code doit être “strict” sur l’enqueue : dépendances explicites, chargement conditionnel, pas de scripts injectés en dur dans le footer.
- REST : permission_callback + nonces ne sont plus “optionnels” si vous exposez des actions (même internes).
- Les hooks au mauvais timing (init vs wp_loaded vs enqueue) deviennent des bugs visibles avec le cache, l’éditeur de blocs et certains builders.
- Thèmes classiques : ils restent viables, mais vous gagnez à adopter des patterns compatibles blocks (templates, styles, variations).
- Divi 5 / Elementor / Avada : la plupart des soucis viennent des assets et du conditionnel (ne chargez pas partout), plus que du HTML.
- Action immédiate utile : audit rapide de vos assets + endpoints REST + snippets “anciens tutos”.
Avant / Après en code
Je prends un cas réel que je dépanne souvent : un plugin (ou functions.php) qui injecte du JS/CSS “comme avant”, puis se retrouve avec des conflits (jQuery pas chargé au bon moment, scripts du builder chargés après, minification qui réordonne tout).
Cas #1 : enqueue d’assets trop “lâche” (problèmes avec cache et builders)
Avant (anti-pattern fréquent)
<?php
// Mauvais exemple : injection directe, pas de dépendances, pas de version, pas de condition.
add_action('wp_footer', function () {
?>
<script>
// Code inline difficile à déboguer, souvent cassé par la minification
jQuery(function($){
$('.cta').on('click', function(){ alert('ok'); });
});
</script>
<?php
});
// Mauvais exemple : style chargé partout, même dans l'admin et le builder
add_action('wp_enqueue_scripts', function () {
wp_enqueue_style('mon-style', get_stylesheet_directory_uri() . '/assets/style.css');
});
Ce qui se passe en coulisses : votre script suppose que jQuery est là, mais vous ne déclarez pas la dépendance. Avec certains caches, l’ordre change. Avec Divi/Elementor, l’éditeur charge ses propres bundles et peut retarder certains scripts. Résultat : bug intermittent, impossible à reproduire “à la demande”.
Après (approche robuste WordPress 6.9.4+)
<?php
/**
* Enqueue propre : dépendances, versioning, conditionnel, et inline script attaché au handle.
* Compatible WordPress 6.9.4+ / PHP 8.1+.
*/
add_action('wp_enqueue_scripts', function () {
// Exemple de condition : ne chargez pas sur tout le site si ce n'est utile que sur une page.
if ( ! is_page('contact') ) {
return;
}
$theme_version = wp_get_theme()->get('Version');
// CSS versionné
wp_enqueue_style(
'mon-style',
get_stylesheet_directory_uri() . '/assets/style.css',
array(),
$theme_version
);
// JS externe versionné + dépendances explicites
wp_enqueue_script(
'mon-cta',
get_stylesheet_directory_uri() . '/assets/cta.js',
array('jquery'),
$theme_version,
array(
'in_footer' => true,
)
);
// Inline script attaché au bon handle (évite l'injection "au hasard" dans le footer)
$inline = <<<JS
jQuery(function($){
$('.cta').on('click', function(){
alert('ok');
});
});
JS;
wp_add_inline_script('mon-cta', $inline, 'after');
}, 20);
Différences clés :
- Dépendances explicites : plus de “ça marche tant que…”.
- Versioning : cache invalidé proprement après mise à jour du thème/plugin.
- Conditionnel : performance + moins de conflits avec les bundles des builders.
- Inline attaché à un handle : ordre garanti et debug facilité.
Cas #2 : REST endpoint trop permissif (et qui finit par poser un vrai risque)
Autre classique : un endpoint REST “interne” pour déclencher une action (purge cache, resync, import). Beaucoup de snippets oubliés n’ont pas de permission_callback solide. Ce n’est pas “juste une bonne pratique” : c’est une surface d’attaque.
Avant (dangereux)
<?php
add_action('rest_api_init', function () {
register_rest_route('monplugin/v1', '/purge', array(
'methods' => 'POST',
'callback' => function () {
// Dangereux : aucune permission, aucune vérification
do_action('monplugin_purge_cache');
return array('ok' => true);
},
));
});
Après (permissions + nonce + réponse standardisée)
<?php
add_action('rest_api_init', function () {
register_rest_route('monplugin/v1', '/purge', array(
'methods' => 'POST',
'callback' => 'monplugin_rest_purge_cache',
'permission_callback' => function (WP_REST_Request $request) {
// 1) Capacité : adaptez selon votre besoin (manage_options est volontairement strict).
if ( ! current_user_can('manage_options') ) {
return false;
}
// 2) Nonce REST : attendu via header X-WP-Nonce côté JS.
$nonce = $request->get_header('X-WP-Nonce');
if ( ! $nonce || ! wp_verify_nonce($nonce, 'wp_rest') ) {
return false;
}
return true;
},
));
});
/**
* Callback REST.
*/
function monplugin_rest_purge_cache(WP_REST_Request $request): WP_REST_Response {
do_action('monplugin_purge_cache');
return new WP_REST_Response(
array(
'ok' => true,
'message' => 'Cache purgé.',
),
200
);
}
Différences clés :
- permission_callback n’est pas décoratif : c’est votre garde-fou.
- Nonce wp_rest : indispensable si vous appelez l’endpoint depuis l’admin (ou un écran protégé).
- Réponse WP_REST_Response : plus stable pour les clients JS (status code, structure).
Impact concret
Pour les blogueurs (intermédiaires)
Vous allez surtout le sentir via vos extensions : une mise à jour WordPress 6.9.x + un plugin qui charge des scripts partout peut ralentir l’éditeur, casser un bouton, ou déclencher des erreurs console uniquement dans certaines pages.
- Symptôme typique : “ça marche sur la page A, pas sur la page B”. Cause fréquente : conditionnel absent, dépendances non déclarées, cache agressif.
- Autre symptôme : “le builder charge mais certains modules sont vides”. Souvent un conflit JS (ordre de chargement, double version d’une lib).
Pour les développeurs
Votre travail se déplace vers de la fiabilité : déclarer, isoler, tester. Les snippets “rapides” restent possibles, mais ils doivent être placés au bon endroit et au bon hook, avec des dépendances propres. J’ai souvent vu des erreurs venir d’un code copié dans le mauvais fichier (functions.php du thème parent au lieu du thème enfant, ou dans un plugin de snippets sans contrôle de version).
- Plugins existants : ceux qui injectent du JS inline, ou qui exposent des endpoints REST sans permission, sont à risque (bugs + sécurité).
- Thèmes classiques : si vous mélangez shortcodes de layout + builder + custom JS, l’ordre de chargement devient critique.
- Thèmes FSE (block themes) : vous gagnez en cohérence si vous migrez une partie du layout vers des patterns/blocks, mais vous devez apprendre à “penser templates” plutôt que “options”.
Impact spécifique Divi 5, Elementor, Avada
Les trois ont un point commun : ils chargent beaucoup d’assets, parfois conditionnellement, et ils ont des modes éditeur/prévisualisation qui ne se comportent pas comme le front.
- Divi 5 : attention aux scripts chargés uniquement sur is_admin() (mauvais test), car Divi a des écrans hybrides. Préférez tester des actions spécifiques (ex : enqueue sur wp_enqueue_scripts + détection de contexte si nécessaire) et évitez d’injecter du JS via wp_footer sans handle.
- Elementor : si vous développez un widget custom, chargez vos assets uniquement quand le widget est présent (ou au minimum sur les pages Elementor). Les collisions viennent souvent d’un script global chargé sur tout le site.
- Avada : Fusion Builder a son écosystème de scripts. Même règle : dépendances explicites + versioning + éviter les bibliothèques chargées deux fois.
Tableau de diagnostic (quand “ça marche puis ça casse”)
| Symptôme | Cause probable | Vérification | Solution |
|---|---|---|---|
| Bouton JS inactif sur certaines pages | Script chargé avant la dépendance (jQuery/Bundle) ou pas chargé du tout | Console navigateur + onglet Network, vérifier l’ordre et la présence des fichiers | wp_enqueue_script avec dépendances + conditionnel + wp_add_inline_script |
| Fonction REST “Forbidden” après mise à jour | permission_callback trop stricte ou nonce absent | Inspecter la requête (header X-WP-Nonce), vérifier current_user_can | Ajouter nonce wp_rest + capacité adaptée + messages d’erreur |
| Éditeur Elementor/Divi lent | Assets globaux trop lourds, chargés partout | Profiler (Performance tab), désactiver temporairement le plugin concerné | Charger uniquement sur les pages/contexts nécessaires |
| Snippet “random” qui casse après update | Hook inadapté (init vs wp_loaded), fonction appelée trop tôt | WP_DEBUG_LOG + stack trace, vérifier ordre d’exécution | Déplacer le hook, ajuster priorité, vérifier que la fonction existe |
| Modifs invisibles malgré déploiement | Cache (plugin/CDN/navigateur) + version d’assets statique | Vider caches + vérifier query string version | Versionner via wp_get_theme()->get('Version') ou filemtime |
Risques, compatibilités et points de vigilance
Ce qui est nouveau (tendance 6.7 → 6.9.4)
- Exigence de propreté sur les assets : WordPress et l’écosystème (builders, caches, optimisation) tolèrent moins les scripts “injectés” hors pipeline.
- Durcissement implicite : ce qui passait “par hasard” passe moins souvent.
- Plus de surfaces REST (plugins, blocs, UI) : donc plus de risques si vos permissions sont floues.
Ce qui change (sans être annoncé comme “breaking”)
- Ordre d’exécution : des hooks mal choisis deviennent des bugs. Exemple classique : enregistrer des scripts trop tôt (init) et les enqueue trop tard, ou inversement.
- Cache et minification : avec HTTP/2/3 + bundling plugins, les hypothèses sur l’ordre des scripts sont fragiles.
- Éditeurs : le back-office n’est plus “un endroit à part”. L’éditeur de site et les builders ressemblent à des apps, donc vos scripts globaux y entrent en collision.
Ce qui casse potentiellement
- Code d’ancien tutoriel : snippets 2017–2021 qui injectent du JS inline, utilisent des hooks approximatifs, ou font du REST sans permission.
- PHP trop ancien : si votre hébergeur traîne, vous aurez des erreurs fatales (typed properties, match, etc.). WordPress 6.9.4 tourne avec PHP 8.1+ recommandé. Vérifiez côté prod.
- Snippets dans le thème parent : mise à jour du thème = perte du code = “ça a disparu”. C’est un “incident” très courant.
Timeline de dépréciation (pragmatique)
Le core déprécie rarement brutalement sans période. Le vrai risque, c’est plutôt : votre code s’appuie sur un comportement non garanti (ordre, dépendances implicites) et finit par tomber. Traitez ça comme une dépréciation de fait.
Ce que je recommande en 2026 : considérez tout code qui injecte des assets en dur (echo <script>) comme “à migrer”, même s’il n’y a pas de notice officielle.
Comment migrer
Étape 1 : audit rapide (30 minutes, très rentable)
- Recherchez dans vos plugins/thèmes :
wp_footer+<script>,echo '<script',admin-ajax.php,register_rest_routesans permission_callback. - Listez les pages où vos assets sont réellement nécessaires (souvent 10–20% du site).
- Activez les logs sur un environnement de staging :
WP_DEBUG,WP_DEBUG_LOG. Évitez de faire ça en prod sans encadrement (risque de fuite d’infos).
<?php
// wp-config.php (staging uniquement)
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false); // Évite d'afficher les erreurs aux visiteurs
Étape 2 : migrer les assets vers des handles (et arrêter l’injection sauvage)
Deux stratégies fiables pour versionner : version de thème/plugin (simple) ou filemtime() (précis, mais attention aux FS distribués).
<?php
add_action('wp_enqueue_scripts', function () {
$file = get_stylesheet_directory() . '/assets/cta.js';
$url = get_stylesheet_directory_uri() . '/assets/cta.js';
// Attention : filemtime() peut être coûteux sur certains hébergements réseau.
$ver = file_exists($file) ? (string) filemtime($file) : wp_get_theme()->get('Version');
wp_enqueue_script(
'mon-cta',
$url,
array('jquery'),
$ver,
array('in_footer' => true)
);
});
Étape 3 : sécuriser REST (capabilities + nonce) et documenter
Si votre endpoint est appelé depuis le front (visiteurs), le nonce “wp_rest” ne suffit pas toujours (et ce n’est pas un mécanisme d’auth publique). Dans ce cas, basculez vers des jetons applicatifs, des signatures, ou limitez strictement les actions. Ne laissez pas un endpoint “purge/import” accessible anonymement.
Étape 4 : vérifier les hooks et priorités (source de bugs “fantômes”)
Erreurs réalistes que je vois passer :
- Utiliser le mauvais hook : enqueuer dans
initau lieu dewp_enqueue_scripts. - Conflit de priorité : votre code s’exécute avant le builder/cache, puis se fait écraser.
- Fonction appelée avant chargement : vous utilisez une fonction d’un plugin alors que le plugin n’a pas encore initialisé ses classes.
<?php
// Exemple : si vous dépendez d'un plugin, évitez d'appeler ses classes trop tôt.
add_action('plugins_loaded', function () {
if ( ! class_exists('MaLib\Client') ) {
// Le plugin dépendant n'est pas chargé ou pas actif
return;
}
// Initialisation sûre ici
}, 20);
Étape 5 : check-list de compatibilité (staging)
- Mettre à jour WordPress vers 6.9.4 sur staging.
- Mettre à jour Divi 5 / Elementor / Avada + addons.
- Vider caches (plugin cache, serveur, CDN) et cache navigateur (souvent oublié).
- Tester : page builder en mode édition, front, mobile, utilisateur non-admin.
- Vérifier les permaliens (ré-enregistrer) si vous touchez aux routes/rewrite.
Faut-il agir maintenant ou attendre ?
Agissez maintenant si vous cochez au moins une case :
- Vous avez des snippets “historiques” (functions.php, plugin de snippets) non testés depuis longtemps.
- Vous exposez des endpoints REST ou des actions AJAX “admin-ajax” pour des tâches sensibles.
- Vous utilisez un builder (Divi 5/Elementor/Avada) et vous avez des scripts custom globaux.
- Vous avez un site à plusieurs rôles (auteurs, contributeurs) : la sécurité REST/capabilities devient non négociable.
Vous pouvez attendre (quelques semaines) si :
- Votre site n’a pas de code custom (ou uniquement via plugins réputés),
- Vous avez un staging et un process de mise à jour mensuel,
- Vous n’exposez aucune surface REST/AJAX custom.
Dans mon expérience, “attendre” sans staging finit par coûter plus cher. Le bon compromis : mise à jour core régulière, mais audit ciblé sur les zones à risque (assets + REST + hooks).
Conseils de maintenance
1) Standardisez votre manière d’ajouter du code
- Évitez de coller des snippets dans le thème parent.
- Préférez un mini-plugin “site” (mu-plugin si pertinent) pour le code métier.
- Si vous utilisez un plugin de snippets, versionnez vos snippets (copie Git) : j’ai déjà vu des snippets se désactiver après une erreur PHP, et personne ne savait “ce qui a changé”.
2) Tests minimaux à planifier à chaque update WordPress
- Front : pages clés, formulaires, tracking.
- Admin : éditeur de blocs, écran du builder, médias.
- Rôles : test avec un compte auteur (pas admin) pour repérer les erreurs de capacités.
- Console JS : au moins une passe rapide (errors/warnings).
3) Performance : chargez moins, mais mieux
- Conditionnez vos assets (page, type de contenu, présence d’un shortcode/bloc).
- Supprimez les bibliothèques chargées deux fois (souvent avec builders + plugin marketing).
- Versionnez correctement pour éviter les “caches collants”.
4) Sécurité : REST/AJAX et nonces
- Chaque action qui modifie quelque chose doit vérifier capabilities + nonce.
- Ne faites pas confiance aux paramètres envoyés côté client. Sanitize/validate systématiquement.
Référence utile côté PHP : PHP password hashing (si vous manipulez des tokens) et, côté WordPress, les fonctions de sécurité listées dans le handbook.
Ressources
- Developer Blog (dev notes WordPress)
- Référence wp_enqueue_script()
- Référence wp_add_inline_script()
- REST API Handbook
- Référence register_rest_route()
- Core Trac (suivi des changements)
- GitHub wordpress-develop (PR/commits)
- Documentation WordPress (utilisateurs + développeurs)
- Notes de version PHP 8.1
FAQ
1) J’ai copié un snippet et j’ai une erreur 500. Je fais quoi en premier ?
Désactivez le snippet (ou renommez le fichier si vous l’avez mis dans un plugin), puis regardez wp-content/debug.log sur staging. L’erreur la plus fréquente reste un point-virgule manquant ou une accolade mal fermée.
2) Où placer du code custom en 2026 : functions.php, plugin, mu-plugin ?
Pour du code “métier” (CTA, REST, tracking interne), je recommande un petit plugin dédié. functions.php est acceptable pour du cosmétique, mais vous risquez de perdre le code en changeant de thème.
3) Mon JS marche en front mais pas dans Elementor/Divi. Pourquoi ?
Parce que l’éditeur n’est pas le front. Les builders chargent des bundles différents et parfois dans un iframe. Chargez vos scripts via wp_enqueue_script, déclarez les dépendances, et évitez les sélecteurs trop globaux.
4) Dois-je encore utiliser jQuery ?
Si votre site en dépend déjà (ou votre builder), oui, mais déclarez la dépendance. Si vous démarrez un nouveau module, préférez du JS moderne sans dépendance, et chargez-le conditionnellement.
5) Pourquoi mon endpoint REST retourne 403 alors que je suis admin ?
Souvent parce que le nonce wp_rest n’est pas envoyé (header X-WP-Nonce), ou parce que vous testez depuis un contexte non authentifié (onglet privé, domaine différent, cache). Vérifiez la requête dans l’onglet Network.
6) Je dois vider quel cache après avoir modifié un script ?
Au minimum : cache du plugin (si présent) + cache serveur/CDN + cache navigateur. Si votre version d’asset ne change pas, le navigateur gardera l’ancienne copie même si vous avez “vidé le cache WordPress”.
7) Mon code ne se lance pas, mais je ne vois aucune erreur.
Vous êtes probablement sur le mauvais hook, ou votre condition is_page() / is_admin() ne correspond pas au contexte. Ajoutez un log temporaire (sur staging) et vérifiez la priorité du hook.
8) Actions vs filtres : ça peut vraiment casser un site ?
Oui. J’ai déjà vu des devs utiliser add_filter sur un hook qui n’applique pas de valeur, et “attendre un retour”. Résultat : rien ne se passe. À l’inverse, retourner quelque chose sur une action ne sert à rien. Vérifiez la doc du hook concerné.
9) Est-ce que WordPress 6.9.4 impose PHP 8.1 ?
WordPress reste pragmatique sur la compatibilité, mais en 2026, PHP 8.1+ est le minimum recommandé pour la stabilité et la sécurité. Beaucoup de plugins modernes supposent déjà 8.1.
10) J’ai modifié des routes / permaliens et j’ai des 404.
Ré-enregistrez les permaliens (Réglages → Permaliens → Enregistrer) et vérifiez vos règles de rewrite. C’est un oubli extrêmement courant après une migration ou un ajout de CPT.