Le cache de page classique met en cache la réponse HTML complète : rapide et simple, mais tout ou rien. Il devient inadapté dès qu’une page contient une zone réellement dynamique (un compte utilisateur, un panier) à côté de zones lourdes mais identiques pour tous les visiteurs, comme un mega-menu ou un widget de derniers articles avec des requêtes complexes. C’est là qu’intervient le cache de fragments : mettre en cache des morceaux de page indépendamment, via le cache d’objets, tout en laissant le reste de la page se générer normalement.
Arborescence type d’une architecture de cache de fragments
theme/
├── inc/
│ ├── cache/
│ │ ├── class-fragment-cache.php
│ │ └── invalidation.php
├── template-parts/
│ ├── mega-menu.php
│ ├── widget-articles-recents.php
│ └── footer-liens.php
└── functions.php
L’idée directrice : chaque composant coûteux à générer dispose de sa propre classe ou fonction de mise en cache, avec une clé et une durée de vie qui lui sont propres, plutôt qu’une seule règle appliquée uniformément à toute la page.
Une classe de cache de fragments réutilisable
class WPM_Fragment_Cache {
public static function get( $key, callable $generator, $ttl = HOUR_IN_SECONDS ) {
$cached = wp_cache_get( $key, 'wpm_fragments' );
if ( false !== $cached ) {
return $cached;
}
$output = $generator();
wp_cache_set( $key, $output, 'wpm_fragments', $ttl );
return $output;
}
}
Usage dans un template part, pour le mega-menu qui recalculait auparavant l’arborescence complète à chaque affichage :
echo WPM_Fragment_Cache::get(
'mega_menu_html',
function () {
ob_start();
wpm_render_mega_menu();
return ob_get_clean();
},
HOUR_IN_SECONDS
);
Sur ce projet, la génération du mega-menu, qui interrogeait sept niveaux de taxonomie à chaque affichage, prenait 340 millisecondes. Une fois mis en cache, la lecture depuis le cache d’objets Redis tombe à environ 4 millisecondes.

La stratégie d’invalidation, le vrai sujet
Un cache de fragments mal invalidé affiche du contenu obsolète, souvent de façon discrète et difficile à détecter en recette. Chaque fragment doit être invalidé par les événements qui le concernent réellement, pas par un délai arbitraire seul :
function wpm_invalidate_menu_cache( $menu_id ) {
wp_cache_delete( 'mega_menu_html', 'wpm_fragments' );
}
add_action( 'wp_update_nav_menu', 'wpm_invalidate_menu_cache' );
function wpm_invalidate_recent_posts_cache( $post_id ) {
if ( wp_is_post_revision( $post_id ) ) {
return;
}
wp_cache_delete( 'widget_articles_recents', 'wpm_fragments' );
}
add_action( 'save_post', 'wpm_invalidate_recent_posts_cache' );
La durée de vie (TTL) reste utile comme filet de sécurité, au cas où un événement d’invalidation serait oublié, mais elle ne doit jamais être la seule ligne de défense contre le contenu obsolète.
Quels composants sont de bons candidats
- Les menus complexes générés à partir de taxonomies ou de requêtes personnalisées.
- Les widgets de barre latérale qui exécutent une requête coûteuse (articles populaires calculés dynamiquement, par exemple).
- Les blocs de pied de page identiques sur tout le site mais reconstruits à chaque chargement.
- Les zones de recommandation de contenu basées sur des calculs de similarité.
Ce que le cache de fragments ne couvre pas
Les blocs dynamiques Gutenberg (comme les blocs de requête ou les blocs interactifs de l’Interactivity API) suivent une logique de rendu différente, souvent liée au contexte de la requête individuelle, et ne se prêtent pas toujours bien à ce mécanisme sans adaptation spécifique.
Un cache de fragments bien conçu se voit à un détail : il devient invisible pour l’équipe éditoriale, qui ne remarque jamais de contenu en retard.
En résumé
Le cache de fragments comble un vide entre le cache de page, trop grossier pour les pages partiellement dynamiques, et l’absence totale de cache. Sa complexité réside presque entièrement dans l’invalidation : bien pensée événement par événement, elle transforme des composants coûteux en quasi-gratuits sans jamais afficher de contenu périmé.