Un annuaire professionnel régional m’a demandé une URL de la forme /annuaire/bretagne/plombier/, filtrable par région et par métier, sans passer par des paramètres de requête classiques du type ?region=bretagne&metier=plombier. Une bonne partie des extensions bricolent ce genre de besoin avec des pages statiques et du contenu généré dynamiquement en PHP. La Rewrite API de WordPress permet une solution bien plus propre, directement intégrée au système de permaliens.
Déclarer la règle de réécriture
add_rewrite_rule() associe une expression régulière à une URL interne exploitable par WordPress, sous la forme d’une chaîne de requête classique. Elle doit être enregistrée sur le hook init.
add_action( 'init', 'annuaire_ajouter_regles_reecriture' );
function annuaire_ajouter_regles_reecriture() {
add_rewrite_rule(
'^annuaire/([^/]+)/([^/]+)/?$',
'index.php?annuaire_region=$matches[1]&annuaire_metier=$matches[2]',
'top'
);
}
Le troisième argument, 'top', place la règle en tête de la liste plutôt qu’à la fin. C’est important : WordPress évalue les règles dans l’ordre, et une règle générique du cœur pourrait sinon intercepter l’URL avant que la vôtre ne soit testée.
Enregistrer les query vars associées
Une règle de réécriture qui pointe vers des variables de requête non enregistrées ne fonctionne pas : WordPress ignore silencieusement toute variable de $_GET qui ne figure pas dans sa liste blanche de query vars publiques.

add_filter( 'query_vars', 'annuaire_ajouter_query_vars' );
function annuaire_ajouter_query_vars( $vars ) {
$vars[] = 'annuaire_region';
$vars[] = 'annuaire_metier';
return $vars;
}
C’est l’erreur numéro un sur ce type de développement : la règle semble correcte, l’URL ne renvoie pas de 404, mais les valeurs de get_query_var( 'annuaire_region' ) restent obstinément vides. Neuf fois sur dix, c’est parce que le filtre query_vars a été oublié.
Intercepter la requête sur template_redirect
Une fois les query vars disponibles, il reste à décider quoi afficher. Deux approches possibles : rediriger vers un template personnalisé via template_include, ou construire la sortie directement sur template_redirect.
add_action( 'template_redirect', 'annuaire_afficher_page_filtree' );
function annuaire_afficher_page_filtree() {
$region = get_query_var( 'annuaire_region' );
$metier = get_query_var( 'annuaire_metier' );
if ( empty( $region ) || empty( $metier ) ) {
return;
}
$professionnels = annuaire_rechercher_professionnels( $region, $metier );
if ( empty( $professionnels ) ) {
global $wp_query;
$wp_query->set_404();
status_header( 404 );
return;
}
annuaire_charger_template_resultats( $region, $metier, $professionnels );
exit;
}
Gérer explicitement le cas d’une combinaison région/métier sans résultat avec set_404() évite d’afficher une page vide avec un code HTTP 200, ce qui serait trompeur autant pour les visiteurs que pour les moteurs de recherche.
Le piège du flush prématuré ou répété
Pour que les nouvelles règles soient prises en compte, WordPress doit régénérer sa table de règles de réécriture, une opération réalisée par flush_rewrite_rules(). C’est une fonction coûteuse, à réserver strictement à l’activation et à la désactivation de l’extension.
- Ne jamais appeler
flush_rewrite_rules()sur le hookinità chaque chargement de page : c’est l’erreur de performance la plus fréquente sur ce sujet - À l’activation, appeler d’abord la fonction qui enregistre les règles, puis
flush_rewrite_rules(), jamais l’inverse - À la désactivation, appeler
flush_rewrite_rules()seul suffit à nettoyer les règles devenues obsolètes
register_activation_hook( __FILE__, function () {
annuaire_ajouter_regles_reecriture();
flush_rewrite_rules();
} );
register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
Un flush systématique à chaque chargement masque un bug de développement plutôt qu’il ne le résout : si une règle ne fonctionne qu’après un flush manuel répété, c’est probablement qu’elle n’est pas enregistrée au bon moment.
Pour aller plus loin
La Rewrite API reste, huit ans après ses débuts, l’outil de référence pour créer des URL métier propres sans dépendre d’un plugin tiers de gestion de permaliens. Elle demande de la rigueur sur l’ordre des opérations — règle, query vars, puis flush une seule fois — mais offre en échange des URL lisibles, indexables, et parfaitement intégrées au reste du système de permaliens de WordPress.