Si vous avez déjà ajouté single.php dans un thème… pour découvrir que WordPress continue d’afficher un autre fichier, vous avez touché du doigt la hiérarchie des templates. Le plus frustrant, c’est que WordPress ne “se trompe” pas : il suit une liste de candidats, et vous ne voyez pas facilement lequel a gagné.

WordPress 6.9.4 (avril 2026) fournit des outils fiables pour rendre cette résolution visible, et surtout pour la maîtriser sans casser votre thème, sans “if/else” partout, et sans vous battre contre un page builder.

Le problème / Le besoin

Besoin typique : vous voulez surcharger l’affichage d’un type de contenu (un article, une page, une archive de catégorie, un CPT, une taxonomie) et vous vous demandez quel fichier créer : single.php, single-post.php, taxonomy.php, category.php, archive.php, front-page.php

Le problème vient de deux choses :

  • la hiérarchie des templates est une cascade de “candidats” (plusieurs fichiers possibles) ;
  • la présence d’un thème enfant, d’un plugin, d’un builder (Divi/Elementor/Avada) ou d’un template personnalisé peut changer le résultat.

À la fin, vous saurez :

  • prévoir quel template WordPress va choisir pour une URL donnée ;
  • voir, dans l’admin et en front, le template réellement utilisé ;
  • ajouter vos propres candidats (sans hacks) via template_include ;
  • déboguer les cas tordus (page d’accueil, page blog, archives, 404, recherche, attachements, templates de page).

Résumé rapide

  • On active un “mode debug templates” via un mini-plugin mu-plugin (copier-coller) compatible WordPress 6.9.4+ et PHP 8.1+.
  • On affiche le template final choisi et les principaux paramètres de requête (type, ID, taxonomie) de façon sécurisée (capability + nonces si nécessaire).
  • On logue dans debug.log (optionnel) pour garder une trace sans polluer l’écran.
  • On ajoute une variante “surcharge contrôlée” via template_include pour injecter un template spécifique selon une règle métier.
  • On couvre les cas Gutenberg / Full Site Editing (templates HTML) et les thèmes classiques (PHP), sans mélanger les deux.

Quand utiliser cette solution

  • Vous développez un thème (classique ou bloc) et vous voulez vérifier quel fichier/quel template est réellement utilisé.
  • Vous intervenez sur un site existant avec un thème enfant et des overrides dispersés.
  • Vous avez un CPT + taxonomies et vous hésitez entre single-{post_type}.php, archive-{post_type}.php, taxonomy-{taxonomy}.php, etc.
  • Vous suspectez un plugin de “prendre la main” sur le template (LMS, e-commerce, membership).
  • Vous voulez documenter proprement une convention interne d’équipe (naming des templates, priorités).

Quand ne PAS utiliser cette solution

  • Vous cherchez juste à changer un texte ou un bloc : privilégiez les patterns, les styles globaux, ou un bloc personnalisé plutôt qu’un nouveau template.
  • Votre besoin est un simple layout : un template de page (éditeur de site / template de page) ou un modèle Elementor/Divi suffit souvent.
  • Vous êtes sur un site très sensible (production à fort trafic) : n’activez pas l’affichage du template en front. Utilisez seulement le logging, et uniquement pour les admins.
  • Vous voulez “forcer” un template pour tout : c’est souvent un anti-pattern. Préférez des règles ciblées, ou des template parts réutilisables.

Prérequis / avant de commencer

Préparez un environnement propre. J’ai souvent vu des snippets de debug ajoutés directement dans functions.php sur production, puis oubliés pendant des mois.

  • WordPress 6.9.4 (ou plus récent) et PHP 8.1+.
  • Accès au FTP/SSH (ou au gestionnaire de fichiers) pour créer un mu-plugin.
  • Activez le debug sur un environnement de staging :
/** wp-config.php (staging uniquement) */
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Sources officielles utiles :

L’approche naïve (et pourquoi l’éviter)

L’approche que je vois le plus : créer des fichiers “au hasard” jusqu’à ce que ça marche, puis ne plus y toucher. Variante : mettre des if ( is_category() ) partout dans index.php.

Exemple naïf : tout gérer dans index.php

<?php
// Mauvaise idée : vous recréez la hiérarchie à la main, et ça devient ingérable.
get_header();

if ( is_single() ) {
	// Affichage single
} elseif ( is_page() ) {
	// Affichage page
} elseif ( is_category() ) {
	// Affichage catégorie
} else {
	// Fallback
}

get_footer();

Pourquoi ça pose problème :

  • Maintenabilité : chaque nouveau cas ajoute une branche, puis des sous-branches.
  • Compatibilité : vous contournez les conventions du thème (template parts, hooks, partials).
  • Plugins : certains plugins s’attendent à des templates dédiés (ex. single-product.php côté WooCommerce) et injectent des hooks spécifiques.
  • Débogage : vous ne savez plus ce qui est “normal” vs “spécial”.

Autre piège : confondre template de page et template de la hiérarchie

Un template de page sélectionné dans l’éditeur (ou via Template dans l’admin) ne se comporte pas comme page.php : il passe avant, mais seulement pour les pages. Beaucoup de bugs viennent de là.

La bonne approche — tutoriel pas à pas

On va rendre la résolution de template observable, puis on va l’exploiter proprement.

Étape 1 — Créez un mu-plugin dédié au debug

Créez le fichier : wp-content/mu-plugins/bpcab-template-debugger.php.

Pourquoi un mu-plugin ? Parce qu’il est chargé tôt et ne dépend pas de l’activation d’un plugin (pratique en dépannage). Doc : Must Use Plugins.

Étape 2 — Ajoutez un filtre template_include pour capturer le template final

template_include reçoit le chemin du template qui va être inclus. C’est le meilleur point d’observation parce que WordPress a déjà fait son choix.

Étape 3 — Affichez l’info uniquement pour les admins

On limite l’affichage aux utilisateurs capables de gérer le site (capability) et on évite d’exposer la structure de fichiers à des visiteurs. C’est un détail de sécurité réel : révéler les chemins et conventions aide un attaquant à cibler un thème vulnérable.

Étape 4 — Ajoutez un “panneau” discret en bas de page

On injecte un petit bloc HTML via wp_footer / admin_bar_menu selon vos préférences. Je préfère le footer : il marche même si l’admin bar est désactivée.

Étape 5 — (Optionnel) Logguez dans debug.log

Utile quand un cache ou un builder masque le footer, ou quand vous testez en headless.

Étape 6 — Comprenez la hiérarchie avec des exemples concrets

Quelques règles que vous utiliserez tout le temps :

  • Single d’un CPT : single-{post_type}.phpsingle.phpsingular.phpindex.php
  • Archive d’un CPT : archive-{post_type}.phparchive.phpindex.php
  • Taxonomie : taxonomy-{taxonomy}-{term}.phptaxonomy-{taxonomy}.phptaxonomy.phparchive.phpindex.php
  • Catégorie (taxonomie category) : category-{slug}.php / category-{id}.phpcategory.phparchive.phpindex.php
  • Page d’accueil : front-page.php (si page statique) ; sinon home.php pour la liste des articles ; sinon index.php

La référence à garder sous la main : developer.wordpress.org — Template Hierarchy.

Code complet

Copiez-collez tel quel dans wp-content/mu-plugins/bpcab-template-debugger.php. Ce code est compatible WordPress 6.9.4+ et PHP 8.1+.

<?php
/**
 * Plugin Name: BPCAB - Template Debugger (MU)
 * Description: Affiche et logue le template WordPress réellement utilisé + infos de requête, pour les administrateurs.
 * Author: BPCAB
 * Version: 1.0.0
 *
 * Installation : wp-content/mu-plugins/bpcab-template-debugger.php
 * Compatibilité : WordPress 6.9.4+, PHP 8.1+
 */

declare(strict_types=1);

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class BPCAB_Template_Debugger {

	/**
	 * Stocke le chemin du template final résolu par WordPress.
	 */
	private static string $template_path = '';

	/**
	 * Active/désactive l'affichage front (laissez false en production).
	 */
	private static bool $show_in_footer = true;

	/**
	 * Active/désactive l'écriture dans debug.log (WP_DEBUG_LOG doit être true).
	 */
	private static bool $log_to_debug_log = false;

	public static function init(): void {
		add_filter( 'template_include', [ __CLASS__, 'capture_template' ], 9999 );

		// Affichage front (discret) : fonctionne avec la plupart des thèmes classiques.
		add_action( 'wp_footer', [ __CLASS__, 'render_footer_panel' ], 9999 );

		// Logging (optionnel) : utile quand le cache masque le HTML.
		add_action( 'shutdown', [ __CLASS__, 'maybe_log' ], 0 );
	}

	/**
	 * Capture le template final.
	 *
	 * @param string $template Chemin absolu vers le fichier template choisi.
	 * @return string Le même template (on ne modifie rien ici).
	 */
	public static function capture_template( string $template ): string {
		self::$template_path = $template;
		return $template;
	}

	/**
	 * Détermine si on peut afficher les infos (sécurité).
	 */
	private static function can_show(): bool {
		// Ne jamais exposer ceci aux visiteurs.
		if ( ! is_user_logged_in() ) {
			return false;
		}

		// Capability stricte : ajustez si vous déléguez à des éditeurs techniques.
		if ( ! current_user_can( 'manage_options' ) ) {
			return false;
		}

		// Évitez l'affichage sur l'admin (on vise le front).
		if ( is_admin() ) {
			return false;
		}

		return true;
	}

	/**
	 * Construit une ligne "contexte" lisible.
	 */
	private static function build_context_summary(): string {
		global $wp_query;

		$parts = [];

		if ( is_404() ) {
			$parts[] = 'is_404';
		}
		if ( is_search() ) {
			$parts[] = 'is_search';
		}
		if ( is_front_page() ) {
			$parts[] = 'is_front_page';
		}
		if ( is_home() ) {
			$parts[] = 'is_home';
		}
		if ( is_singular() ) {
			$parts[] = 'is_singular';
		}
		if ( is_single() ) {
			$parts[] = 'is_single';
		}
		if ( is_page() ) {
			$parts[] = 'is_page';
		}
		if ( is_archive() ) {
			$parts[] = 'is_archive';
		}
		if ( is_category() ) {
			$parts[] = 'is_category';
		}
		if ( is_tag() ) {
			$parts[] = 'is_tag';
		}
		if ( is_tax() ) {
			$parts[] = 'is_tax';
		}
		if ( is_author() ) {
			$parts[] = 'is_author';
		}
		if ( is_date() ) {
			$parts[] = 'is_date';
		}

		// Infos requête utiles.
		$post_id = get_queried_object_id();
		if ( $post_id ) {
			$parts[] = 'queried_id=' . (int) $post_id;
		}

		$q_obj = get_queried_object();
		if ( $q_obj instanceof WP_Post ) {
			$parts[] = 'post_type=' . $q_obj->post_type;
			$slug = $q_obj->post_name ?? '';
			if ( $slug !== '' ) {
				$parts[] = 'post_slug=' . $slug;
			}

			// Template de page choisi (si page).
			if ( $q_obj->post_type === 'page' ) {
				$page_tpl = get_page_template_slug( $q_obj );
				if ( $page_tpl ) {
					$parts[] = 'page_template=' . $page_tpl;
				}
			}
		} elseif ( $q_obj instanceof WP_Term ) {
			$parts[] = 'taxonomy=' . $q_obj->taxonomy;
			$parts[] = 'term=' . $q_obj->slug;
		} elseif ( $q_obj instanceof WP_User ) {
			$parts[] = 'author=' . $q_obj->user_login;
		}

		// Ajoute quelques flags WP_Query.
		if ( isset( $wp_query ) && $wp_query instanceof WP_Query ) {
			$parts[] = 'paged=' . (int) max( 1, get_query_var( 'paged' ) );
		}

		return implode( ' | ', $parts );
	}

	/**
	 * Rend un panneau en bas de page.
	 */
	public static function render_footer_panel(): void {
		if ( ! self::$show_in_footer ) {
			return;
		}
		if ( ! self::can_show() ) {
			return;
		}

		$template = self::$template_path;

		// Cas edge : certains contextes très tôt peuvent ne pas avoir de template capturé.
		if ( $template === '' ) {
			$template = '(template non capturé)';
		}

		$theme = wp_get_theme();
		$theme_label = $theme->get( 'Name' ) . ' ' . $theme->get( 'Version' );

		// On évite d'afficher un chemin absolu complet (information sensible).
		// On tente de le rendre relatif à ABSPATH si possible.
		$relative_template = $template;
		if ( $template !== '' && str_starts_with( $template, ABSPATH ) ) {
			$relative_template = substr( $template, strlen( ABSPATH ) );
		}

		$context = self::build_context_summary();

		?>
		<div style="position:fixed;left:0;right:0;bottom:0;z-index:999999;background:#111;color:#fff;padding:10px 12px;font:12px/1.4 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;border-top:1px solid rgba(255,255,255,.15)">
			<div style="max-width:1200px;margin:0 auto;display:flex;gap:14px;flex-wrap:wrap;align-items:flex-start">
				<div><strong>Template</strong> : <code style="color:#9ef"><?php echo esc_html( $relative_template ); ?></code></div>
				<div><strong>Contexte</strong> : <span><?php echo esc_html( $context ); ?></span></div>
				<div><strong>Thème</strong> : <span><?php echo esc_html( $theme_label ); ?></span></div>
			</div>
		</div>
		<?php
	}

	/**
	 * Loggue le template et le contexte dans debug.log (si activé).
	 */
	public static function maybe_log(): void {
		if ( ! self::$log_to_debug_log ) {
			return;
		}
		if ( ! self::can_show() ) {
			return;
		}

		$template = self::$template_path ?: '(template non capturé)';
		$context  = self::build_context_summary();

		// error_log écrit dans debug.log si WP_DEBUG_LOG est activé (sinon, comportement dépend du serveur).
		error_log( '[BPCAB Template Debugger] template=' . $template . ' context=' . $context );
	}
}

BPCAB_Template_Debugger::init();

Explication du code

Ce que fait le code, simplement

  • Il écoute le moment où WordPress a déjà décidé du template final.
  • Il mémorise ce chemin dans une variable statique.
  • Il affiche un panneau bas de page (uniquement pour les admins).
  • Il peut écrire une ligne dans debug.log si vous activez l’option.

Pourquoi template_include est le bon point d’accroche

template_include est documenté ici : developer.wordpress.org/reference/hooks/template_include.

En pratique, c’est le dernier moment “propre” pour observer/altérer le template. Avant, vous pouvez influencer la requête, mais vous n’avez pas encore le fichier final. Après, c’est trop tard.

Sécurité : capability et exposition d’informations

On vérifie is_user_logged_in() et current_user_can('manage_options'). Doc : current_user_can().

Pourquoi être strict ? Parce que le chemin d’un template (surtout absolu) révèle :

  • le nom du thème actif ;
  • la structure de dossiers ;
  • parfois un chemin système (utile pour du ciblage).

Vous remarquerez aussi qu’on convertit le chemin absolu en relatif à ABSPATH quand c’est possible.

Pourquoi on n’utilise pas “des fonctions magiques” pour lister tous les candidats

Il existe des fonctions internes qui construisent la liste de templates possibles selon le contexte, mais elles ne sont pas toutes exposées comme API publique stable. Avec WordPress 6.9.4, je préfère une approche robuste : observer le résultat final, puis raisonner avec la doc de hiérarchie.

Si vous voulez vraiment une liste “candidats”, faites-le par contexte (singular, archive, taxonomy) et construisez vos propres candidats, plutôt que de dépendre d’un détail interne susceptible d’évoluer.

Variantes et cas d’usage

Variante 1 — Forcer un template pour un cas métier (proprement)

Vous avez un besoin réel : par exemple, toutes les pages enfants de “Documentation” doivent utiliser un template spécial, même si l’auteur n’a pas choisi de template de page.

Ajoutez ce code dans un plugin classique (ou votre mu-plugin) et placez un fichier page-docs.php dans votre thème enfant.

<?php
/**
 * Forcer un template spécifique pour les pages sous un parent donné.
 * À mettre dans un plugin (recommandé) ou mu-plugin.
 */

declare(strict_types=1);

add_filter( 'template_include', function( string $template ): string {

	if ( ! is_page() ) {
		return $template;
	}

	$post_id = get_queried_object_id();
	if ( ! $post_id ) {
		return $template;
	}

	$parent_id = wp_get_post_parent_id( $post_id );
	if ( ! $parent_id ) {
		return $template;
	}

	// Exemple : parent = page "Documentation" ID 123
	if ( (int) $parent_id !== 123 ) {
		return $template;
	}

	// Cherche page-docs.php dans le thème enfant/parent.
	$forced = locate_template( [ 'page-docs.php' ], false, false );

	// locate_template retourne une chaîne vide si introuvable.
	if ( $forced ) {
		return $forced;
	}

	return $template;

}, 50 );

Notes :

  • locate_template() respecte le thème enfant puis le parent. C’est exactement ce que vous voulez en surcharge.
  • Évitez de renvoyer un chemin “en dur” hors du thème : vous cassez la portabilité.
  • Gardez une priorité raisonnable (ici 50) pour laisser d’autres plugins agir, mais passer avant des overrides “tardifs”.

Variante 2 — Debug ciblé par paramètre URL (sans panneau permanent)

Sur des sites avec cache agressif, j’ai souvent vu le panneau rester “collé” dans une version mise en cache. Même si vous limitez aux admins, certains caches de page mal configurés peuvent servir la page admin-cachée à d’autres (oui, ça arrive).

Solution : n’afficher que si ?tpldebug=1 est présent, et seulement pour les admins.

<?php
add_action( 'wp_footer', function(): void {
	if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
		return;
	}

	$enabled = isset( $_GET['tpldebug'] ) && $_GET['tpldebug'] === '1';
	if ( ! $enabled ) {
		return;
	}

	// Ici vous pourriez appeler votre rendu (ou réutiliser la classe de l'exemple).
	echo '<div style="position:fixed;bottom:0;left:0;right:0;background:#000;color:#fff;padding:8px;z-index:999999">tpldebug actif</div>';
}, 9999 );

Si vous poussez cette variante plus loin, ajoutez un nonce. Pour un simple affichage sans action, ce n’est pas strictement nécessaire, mais ça évite des surprises si vous ajoutez ensuite un bouton “copier” ou “log”.

Variante 3 — Thèmes blocs (FSE) : distinguer template PHP vs template HTML

Sur un thème bloc, WordPress peut utiliser des templates .html (dans templates/) plutôt que des fichiers PHP classiques. Le filtre template_include vous montrera quand même un fichier PHP “pont” dans certains cas, ce qui peut surprendre.

Mon réflexe : regardez aussi l’éditeur de site (Apparence → Éditeur) et le template assigné. Les templates HTML vivent dans le thème, mais peuvent être surchargés en base (modifications utilisateur).

Pour la partie officielle : Block Themes et Templates (Themes).

Compatibilité Divi 5 / Elementor / Avada

La hiérarchie des templates continue d’exister avec un builder, mais l’affichage final peut être “capturé” par :

  • un template de page assigné (ex. “Elementor Full Width”) ;
  • un header/footer gérés par le builder ;
  • des conditions de thème/builder (Theme Builder, templates globaux).

Divi 5

Divi utilise souvent des templates globaux (Theme Builder) qui s’appliquent par conditions. Votre fichier single.php peut être appelé, mais le contenu est remplacé par la mise en page Divi.

  • Le panneau “Template” reste utile : il vous dit quel fichier PHP sert de base.
  • Si vous ne voyez pas le panneau, testez avec ?tpldebug=1 et désactivez temporairement la minification/caching de Divi.

Elementor

Elementor peut rendre le contenu via ses templates (Single, Archive). WordPress choisit quand même un template PHP, mais Elementor injecte sa sortie via hooks.

  • Si vous forcez un template via template_include, vérifiez que vous ne cassez pas la condition Elementor attendue (souvent liée à the_content).
  • Si le site utilise “Hello Elementor”, la hiérarchie est minimaliste : votre debug est particulièrement utile pour savoir sur quel fallback vous êtes tombé.

Avada (Fusion Builder)

Avada a une couche de templates et d’options globales qui influence les en-têtes, pieds, et layouts d’archives.

  • Le debug par footer fonctionne en général, mais Avada peut optimiser/concaténer le HTML : privilégiez le logging si l’affichage est instable.
  • Après un changement de template, videz aussi les caches Avada (pas seulement le cache navigateur).

Vérifications après mise en place

Voici une check-list rapide que j’utilise en recette.

  • Connectez-vous en admin et ouvrez :
  1. une page standard ;
  2. un article ;
  3. une archive de catégorie ;
  4. une archive de tag ;
  5. la page d’accueil ;
  6. une 404 (URL inexistante).
  • Vérifiez que le panneau affiche un template cohérent (ex. single.php sur un article si pas de single-post.php).
  • Si vous avez activé le log, vérifiez wp-content/debug.log.
  • Si vous forcez un template, testez un cas qui ne doit pas être affecté (anti-régression).

Tableau de diagnostic (rapide)

Symptôme Cause probable Vérification Solution
Panneau non visible Hook wp_footer absent du thème Inspectez le thème : wp_footer() est-il appelé ? Ajoutez wp_footer() dans footer.php (thème enfant) ou utilisez l’admin bar / logging
Template affiché “(non capturé)” Contexte atypique / arrêt avant template_include Regardez les erreurs PHP / fatal Corrigez l’erreur, puis retestez ; activez le logging
Le template change après avoir vidé le cache Cache de page / cache serveur Testez en navigation privée + désactivez cache plugin Videz tous les caches (plugin, serveur, CDN) et retestez
Vous créez single.php mais WordPress utilise autre chose Un template plus spécifique existe Recherchez single-post.php, singular.php, templates builder Supprimez/ajustez le template plus spécifique ou modifiez-le
Archive CPT utilise index.php archive-{post_type}.php absent Vérifiez la présence des fichiers dans le thème enfant Ajoutez archive-{post_type}.php ou archive.php

Si ça ne marche pas

Procédure que j’applique quand un site “refuse” d’utiliser le template attendu.

1) Vérifiez que vous modifiez le bon thème

  • Apparence → Thèmes : thème actif ? thème enfant ?
  • Le fichier est-il dans le thème enfant (recommandé) ou dans le parent ?

Erreur classique : copier single.php dans le parent alors que le site utilise un enfant (ou l’inverse).

2) Vérifiez les permaliens

Quand vous ajoutez un CPT/taxonomie ou changez des règles, régénérez : Réglages → Permaliens → Enregistrer. Je vois encore ce piège en 2026, surtout après import/migration.

3) Désactivez temporairement les plugins “template heavy”

Membership, LMS, SEO, ou plugins de redirection peuvent altérer la requête. Testez en staging, pas en prod.

4) Videz tous les caches

  • cache plugin (page/object) ;
  • cache serveur (Varnish/nginx) ;
  • CDN ;
  • cache builder (Divi/Avada) ;
  • cache navigateur.

5) Cherchez un override via template_include

Si un plugin force un template, votre fichier ne sera jamais choisi. Faites une recherche dans le code (ripgrep/grep) :

grep -R "template_include" -n wp-content/plugins wp-content/themes

6) Vérifiez les erreurs PHP et la version PHP

Un fatal error peut interrompre l’exécution avant que votre debug s’affiche. Et oui : j’ai déjà vu un site “en PHP 7.4” encore en 2026 sur un vieux serveur, ce qui casse des snippets modernes (typage, str_starts_with, etc.).

Pièges et erreurs courantes

Erreur Cause Solution
Copier le code dans functions.php du mauvais thème Thème enfant actif mais modification dans le parent (ou inverse) Ajoutez le mu-plugin (recommandé) ou modifiez le bon thème enfant
Écran blanc après ajout du snippet Point-virgule manquant, accolade mal fermée, PHP < 8.0 Consultez debug.log, corrigez la syntaxe, vérifiez PHP 8.1+
Le panneau ne s’affiche jamais Le thème n’appelle pas wp_footer() Corrigez footer.php (thème enfant) ou utilisez le logging
Le panneau s’affiche mais avec un template inattendu Template plus spécifique existe (ex. single-post.php) Recherchez les fichiers plus spécifiques et comprenez la cascade
Vous forcez un template et Elementor/Divi “perd” sa mise en page Vous bypassiez le template que le builder attend Forcez seulement dans des contextes précis, ou utilisez les conditions du builder
Confusion actions/filtres Retourner une valeur dans une action, ou echo dans un filtre Rappelez-vous : template_include doit retourner un chemin
Le debug reste visible après désactivation Cache de page/CDN sert une version ancienne Videz caches + purge CDN, changez l’URL (cache-busting) pour retester
Snippet cassé par un plugin de snippets Chargement tardif / ordre différent / erreur fatale Préférez un mu-plugin pour le debug, plus stable

Conseils sécurité, performance et maintenance

  • Sécurité : ne montrez jamais de chemins de fichiers aux visiteurs. Gardez la restriction manage_options et évitez l’affichage en production.
  • Performance : le panneau fait peu de travail, mais évitez de calculer des choses coûteuses (ex. requêtes SQL) dans wp_footer.
  • Maintenance : utilisez un mu-plugin pour les outils de debug, et documentez-le. Un snippet “oublié” dans functions.php finit toujours par être copié sur un autre site.
  • Compat future : privilégiez les hooks publics (comme template_include) plutôt que des fonctions internes non documentées.
  • SEO : l’affichage du panneau n’est pas visible aux bots si vous le limitez aux admins. Ne l’affichez pas côté public, même en commentaire HTML.

Ressources

FAQ

Quel est le “dernier recours” dans la hiérarchie ?

index.php. Si vous n’avez rien d’autre, WordPress finit là. C’est pratique, mais ça masque souvent un manque de templates spécifiques.

Pourquoi WordPress utilise front-page.php au lieu de home.php ?

Si une page statique est définie comme page d’accueil, front-page.php est prioritaire. home.php sert plutôt la page “blog posts index”. Le couple “page d’accueil / page des articles” est une source d’erreurs récurrente.

J’ai créé category.php, mais WordPress prend archive.php. Comment est-ce possible ?

La cause la plus fréquente : vous n’êtes pas sur une catégorie mais sur une autre archive (taxonomie personnalisée, date, auteur). Vérifiez le “Contexte” dans le panneau : is_category doit apparaître.

Est-ce que template_include peut casser un plugin ?

Oui. Si vous renvoyez un template qui n’appelle pas les hooks attendus (ou qui ne fait pas the_content()), vous pouvez casser un builder, un plugin SEO, ou un plugin de tracking. Forcez uniquement quand vous avez une règle claire et testée.

Le panneau n’apparaît pas avec mon thème. Dois-je abandonner ?

Non. Deux pistes : (1) votre thème n’appelle pas wp_footer() (bug de thème), (2) un cache/minifier supprime/diffère le footer. Activez le logging ($log_to_debug_log) et/ou utilisez la variante ?tpldebug=1.

Comment savoir si un template de page est sélectionné ?

Pour une page, le code affiche page_template=... via get_page_template_slug(). Si c’est vide, la page utilise la hiérarchie classique (page.php, singular.php, etc.).

Est-ce compatible avec les thèmes blocs (templates HTML) ?

Oui pour l’observation “fichier final inclus”, mais l’interprétation demande de distinguer ce qui vient des templates HTML et ce qui vient du pont PHP. Quand vous êtes en FSE, complétez toujours avec l’éditeur de site pour voir quel template (HTML) est assigné.

Puis-je afficher la liste complète des candidats que WordPress a testés ?

Pas de façon 100% fiable via une API publique unique. Le plus stable est d’observer le template final, puis de déduire les candidats via la doc officielle, ou de construire une liste de candidats pour votre contexte (singular/archive/taxonomy) si vous avez un besoin d’audit.

Pourquoi mon single-{post_type}.php n’est jamais utilisé ?

Causes typiques : (1) vous n’êtes pas sur ce post type (vérifiez post_type= dans le panneau), (2) le contenu est rendu via une page “proxy” (builder), (3) un plugin force un template via template_include.

Quelle est la meilleure façon de “surcharger” un template sans toucher au thème parent ?

Thème enfant + fichiers de templates ciblés, ou un plugin qui force un template via template_include (avec locate_template()). Évitez de modifier le thème parent : la mise à jour écrasera tout.