Pourquoi un appel à wp_redirect(), placé au bon endroit dans un hook template_redirect, ne redirige-t-il jamais l’utilisateur, sans la moindre trace d’erreur dans les journaux ni dans l’écran habituellement bavard de WordPress ? Le code semble correct, la condition qui déclenche la redirection est bien atteinte, un error_log() placé juste avant confirme même que la ligne est exécutée. Et pourtant, la page continue de s’afficher normalement.
Ce scénario, rencontré sur un thème classique dont le fichier functions.php avait été modifié par plusieurs personnes au fil des mois, cache une cause précise et redoutablement discrète : un ou plusieurs caractères présents avant la balise d’ouverture <?php, ou après la balise de fermeture ?> en fin de fichier.
Le symptôme : une redirection qui s’exécute mais ne redirige rien
Le code incriminé ressemble à ceci, et n’a rien d’anormal en apparence :
add_action( 'template_redirect', function () {
if ( is_page( 'ancienne-page' ) ) {
wp_redirect( home_url( '/nouvelle-page/' ), 301 );
exit;
}
} );
La fonction is_page() retourne bien true, l’instruction exit est atteinte, mais le navigateur affiche malgré tout le contenu de la page d’origine, parfois précédé d’un espace blanc supplémentaire en haut de la fenêtre.
Le diagnostic : des en-têtes HTTP déjà envoyés
wp_redirect() repose sur la fonction PHP native header(), qui doit impérativement être appelée avant tout envoi de contenu au navigateur. Or, dès qu’un caractère — un espace, une ligne vide, un caractère invisible d’encodage BOM (byte order mark) — se trouve en dehors des balises <?php ?> d’un fichier chargé avant le hook template_redirect, PHP considère ce caractère comme du contenu à afficher et l’envoie immédiatement au navigateur. Une fois cette sortie effectuée, les en-têtes HTTP sont considérés comme déjà envoyés, et tout appel ultérieur à header() échoue silencieusement : PHP ne lève une erreur « headers already sent » que si l’affichage des erreurs est activé, et cette erreur n’apparaît pas toujours dans les journaux si display_errors est désactivé en production.

Retrouver le caractère fautif
Le suspect le plus fréquent est une ligne vide laissée après la balise ?> à la fin du fichier functions.php, ou un espace introduit avant <?php lors d’un copier-coller depuis un éditeur de texte qui a conservé un caractère invisible. Un moyen fiable de le confirmer consiste à activer temporairement l’affichage des erreurs et à observer le message qui précède l’échec :
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', true );
Le message attardé ressemble à : Warning: Cannot modify header information - headers already sent by (output started at .../functions.php:1). Le numéro de ligne indiqué — souvent la ligne 1 — pointe directement vers le fichier et l’endroit où la sortie a commencé.
Le correctif : supprimer la balise de fermeture PHP
La pratique recommandée dans les standards de codage de WordPress consiste à ne jamais fermer la balise PHP à la fin d’un fichier qui ne contient que du code PHP, précisément pour éviter ce genre d’incident :
<?php
// Fin du fichier functions.php, sans balise de fermeture ni ligne vide après.
add_action( 'template_redirect', function () {
// ...
} );
Il faut également vérifier qu’aucun caractère BOM n’a été enregistré en tête de fichier, ce qui arrive fréquemment avec certains éditeurs configurés en encodage UTF-8 avec BOM plutôt qu’en UTF-8 sans BOM. La plupart des éditeurs modernes permettent de choisir explicitement l’encodage de sauvegarde dans leurs préférences.
Prévenir la récidive avec un outil d’analyse statique
Un outil comme PHP_CodeSniffer, configuré avec les règles du projet WordPress Coding Standards, détecte automatiquement la présence de code en dehors des balises PHP et peut être intégré à une étape de vérification avant chaque déploiement. Cela évite qu’un futur copier-coller, effectué par une autre personne de l’équipe, ne reproduise le même incident sur un autre fichier du thème.
- Supprimer systématiquement la balise de fermeture
?>dans les fichiers 100 % PHP. - Enregistrer les fichiers en UTF-8 sans BOM.
- Vérifier avec un éditeur hexadécimal ou
xxden cas de doute persistant sur les premiers octets du fichier.
En résumé
Une redirection qui s’exécute sans effet, sans message d’erreur visible en production, mérite de vérifier en priorité l’état des en-têtes HTTP avant de remettre en cause la logique métier du code. Le caractère fautif est souvent invisible à l’œil nu dans un éditeur classique, mais son effet est systématique : tant qu’il subsiste avant la première balise <?php d’un fichier chargé tôt dans le cycle de WordPress, aucune fonction basée sur header() ne pourra fonctionner correctement sur les pages concernées.