molitor.photography

RewriteRule et RewriteCond : les règles de réécriture qui fonctionnent vraiment

2026-11-13

Il y a peu de modules Apache qui inspirent autant de respect mêlé de méfiance que mod_rewrite. On le croise partout, dans les tutoriels WordPress, dans les configurations e-commerce, dans les vieux fichiers .htaccess hérités de migrations successives. Et à chaque fois, la même question revient : « mais pourquoi cette règle ne marche pas ? ». La vérité, c'est que mod_rewrite n'est pas difficile en soi. C'est un outil puissant, avec une syntaxe précise, et dont les erreurs viennent presque toujours des mêmes causes. Une fois qu'on a compris le moteur et les drapeaux essentiels, on écrit des règles qui fonctionnent du premier coup. En 2026, avec la multiplication des sites et des architectures proxy, cette compétence reste l'une des plus utiles du métier.

Mon objectif ici n'est pas de vous transformer en expert mondial de la réécriture, mais de vous donner les clés pratiques. On va voir la syntaxe, les drapeaux, les conditions, les pièges et les exemples concrets. Je vais vous montrer mes méthodes de test et de diagnostic, celles que j'utilise encore aujourd'hui. Et vous verrez : la réécriture d'URL n'est pas une magie noire. C'est juste une discipline, avec un peu de pratique.

Pourquoi mod_rewrite inspire autant de crainte, et pourquoi c'est injustifié

La réputation de mod_rewrite vient en grande partie de la documentation officielle, très dense, et des messages d'erreur peu bavards. Ajoutez à cela des règles copiées-collées sans compréhension et vous obtenez une légende tenace. Mais dans la pratique, 90 % des besoins se résument à quelques règles simples. C'est le dernier 10 % qui demande de l'expérience, et encore, souvent pour des cas spécifiques.

Ce que mod_rewrite fait réellement, sans le mythe

À la base, mod_rewrite modifie l'URL demandée par le client. Il peut la transformer en une autre URL interne (réécriture), la rediriger vers une URL externe (redirection), la bloquer, ou la passer vers un proxy. Cette transformation s'appuie sur des expressions régulières, des conditions et des drapeaux. Rien de plus. Tout le reste est une question de combinaison. Une fois qu'on a accepté cette simplicité, la peur recule et la pratique commence.

Les alternatives à envisager avant de sortir l'artillerie lourde

Avant de sortir mod_rewrite, il faut se poser la question : existe-t-il un outil plus simple ? Pour une redirection simple, Redirect ou RedirectMatch de mod_alias suffisent souvent. Pour une URL d'alias, Alias ou AliasMatch sont plus lisibles. Pour forcer HTTPS sur un VirtualHost, une seule RewriteRule suffit, mais parfois un Redirect permanent dans le port 80 fait l'affaire. Sortir mod_rewrite pour un cas trivial, c'est comme utiliser un marteau-pilon pour enfoncer une punaise. Ça marche, mais c'est disproportionné.

Comprendre le moteur avant d'écrire une règle

Avant même d'écrire une règle, il faut comprendre dans quel ordre Apache traite les requêtes dans le module. C'est ce qui détermine quand vos règles s'appliquent et ce qu'elles modifient. Sans cette compréhension, on bricole et on prie.

Le cycle de réécriture : entrée, traitement, sortie

Quand une requête arrive, Apache passe par plusieurs phases : traduction d'URL, autorisation, authentification, puis traitement. mod_rewrite intervient dans la phase de traduction, c'est-à-dire avant que le serveur ne sache quel fichier servir. C'est un détail crucial : si vous placez vos règles dans un bloc <Location>, elles s'appliquent après traduction, dans un autre contexte. La conséquence est que les règles placées dans un <Location> ne se comportent pas comme celles placées dans un <Directory>, un VirtualHost ou un .htaccess. La documentation le mentionne, mais on l'oublie souvent.

Ce qui distingue la réécriture interne de la redirection externe

Une réécriture interne modifie l'URL côté serveur, sans que le navigateur ne s'en rende compte. L'URL affichée reste la même, mais le serveur sert un autre fichier. Une redirection externe, à l'inverse, renvoie une réponse HTTP au client, qui fait une nouvelle requête. La première est invisible, la seconde est explicite. En pratique, une règle de réécriture vers index.php est interne. Une règle qui force HTTPS est externe. Confondre les deux produit des résultats étranges, avec des boucles ou des URL visibles non désirées.

Le contexte : global, VirtualHost, Directory, .htaccess

RewriteRule peut apparaître dans plusieurs contextes : global, VirtualHost, Directory et .htaccess. Selon le contexte, le chemin auquel s'applique la règle change. Dans un fichier .htaccess ou un bloc <Directory>, le chemin est relatif au répertoire concerné, sans slash initial. Dans un VirtualHost ou au global, il commence par un slash et contient le chemin complet de l'URL. Ce détail est la cause numéro un des règles qui « marchent en local mais pas sur le serveur ». Avant d'écrire la règle, sachez dans quel contexte elle est lue, et adaptez votre motif en conséquence.

RewriteRule : la syntaxe décortiquée

La directive RewriteRule prend trois arguments : un motif, une substitution et éventuellement des drapeaux. Une fois qu'on a compris ces trois parties, on peut écrire n'importe quelle règle simple. Et les règles simples couvrent la majorité des besoins.

Les trois arguments : motif, substitution, drapeaux

Le motif est une expression régulière qui s'applique à l'URL demandée. La substitution est ce qui remplace la partie correspondante. Les drapeaux modifient le comportement de la règle. Par exemple, RewriteRule ^article/([0-9]+)$ /index.php?id=$1 [L] réécrit une URL de type /article/42 vers /index.php?id=42, et le drapeau [L] indique d'arrêter là. C'est la base, et à partir de là, on peut tout construire.

Les expressions régulières utiles, sans devenir expert

Pas besoin d'être un gourou des regex pour écrire des règles efficaces. Quelques motifs suffisent dans la plupart des cas :

  • ^ : début de chaîne
  • $ : fin de chaîne
  • . : n'importe quel caractère
  • .* : n'importe quelle séquence
  • [0-9]+ : un ou plusieurs chiffres
  • ([^/]+) : une séquence sans slash, capturée
  • ? : caractère optionnel
  • | : alternative

Les parenthèses capturent une portion, que l'on peut réutiliser avec $1, $2, etc. C'est la base de toute réécriture un peu évoluée. Et surtout, n'utilisez pas les regex complexes sans nécessité. Une règle illisible est une règle qui sera mal maintenue, et qui finira par poser problème.

Le préfixe de substitution et le rôle du slash

Dans un VirtualHost, la substitution commence typiquement par un slash, par exemple /index.php. Dans un fichier .htaccess, la substitution est relative, par exemple index.php. Un slash de trop ou de moins, et la règle échoue. C'est un des points qui m'ont fait perdre le plus de temps au début. Aujourd'hui, je vérifie systématiquement le contexte avant d'écrire la substitution. Un réflexe simple, qui évite bien des erreurs.

RewriteCond : poser les bonnes conditions

Une règle peut être conditionnée par RewriteCond, placée juste au-dessus. Cela permet d'appliquer une règle uniquement si certaines conditions sont remplies. C'est là que la puissance de mod_rewrite se révèle vraiment.

La syntaxe : TestString, CondPattern, drapeaux

RewriteCond prend une chaîne à tester, un motif de condition et des drapeaux optionnels. La chaîne peut être une variable serveur, comme %{HTTP_HOST} ou %{REQUEST_URI}. Le motif est une expression régulière, qui peut commencer par ! pour inverser le résultat. Les drapeaux courants sont [NC] pour ignorer la casse, [OR] pour combiner plusieurs conditions en « ou », et [AND] pour les combiner en « et ». Sans drapeau, les conditions sont combinées en « et » par défaut.

Les variables serveur les plus utiles

Voici celles que j'utilise le plus souvent, avec leur rôle :

Variable Contenu
%{HTTP_HOST} Nom d'hôte demandé par le client, par exemple example.com
%{REQUEST_URI} Chemin de l'URL demandée, sans le nom de domaine
%{QUERY_STRING} Chaîne de paramètres, après le ?
%{REQUEST_FILENAME} Chemin réel du fichier interrogé, utile pour tester son existence
%{HTTPS} on si la connexion est en HTTPS, vide sinon
%{HTTP_USER_AGENT} Chaîne de l'agent utilisateur, utile pour bloquer certains robots
%{REMOTE_ADDR} Adresse IP du client
%{SERVER_PORT} Port du serveur, utile pour distinguer 80 et 443

Les motifs spéciaux : -f, -d, -s, -x

Ces motifs, utilisés à la place d'une expression régulière, testent la nature du fichier :

  • -f : vrai si le chemin correspond à un fichier régulier
  • -d : vrai si c'est un répertoire
  • -s : vrai si c'est un fichier non vide
  • -x : vrai si c'est exécutable

Ils servent surtout dans des règles du type « réécrire vers index.php si le fichier demandé n'existe pas », ce qui est la base des URL propres dans WordPress, Laravel ou Symfony. Un exemple typique : RewriteCond %{REQUEST_FILENAME} !-f puis RewriteCond %{REQUEST_FILENAME} !-d, suivis de la règle qui renvoie vers l'index. Cette combinaison est si courante qu'elle mérite d'être connue par cœur.

Les drapeaux qui changent tout

Les drapeaux modifient le comportement d'une règle. Ils s'écrivent entre crochets à la fin de la directive, séparés par des virgules. Certains sont indispensables, d'autres sont utiles dans des cas précis. Voici les plus importants, avec ce qu'ils font vraiment.

[L] : arrêter là, ou pas vraiment

[L] signifie « last », c'est-à-dire arrêter le traitement des règles suivantes. En pratique, cela fonctionne différemment dans un VirtualHost et dans un .htaccess. Dans un VirtualHost, le traitement des règles s'arrête effectivement. Dans un .htaccess, le module redémarre le cycle sur la nouvelle URL, ce qui peut produire des comportements surprenants. C'est une source classique de boucles. La solution : utiliser [END] dans un .htaccess si vous voulez vraiment arrêter.

[R=301] et [R=302] : Google, navigateurs et cache

[R=301] effectue une redirection permanente, mise en cache par les navigateurs et les moteurs de recherche. [R=302] est une redirection temporaire, non cachée durablement. Le choix entre les deux a un impact SEO considérable. Un 301 mal placé est très difficile à annuler côté clients, parce que les navigateurs le mettent en cache. Je recommande toujours de tester avec [R=302] avant de basculer en [R=301]. C'est une précaution simple qui évite des accidents de redirection.

[NC], [QSA], [NE] : les trois que l'on oublie tout le temps

[NC] rend la comparaison insensible à la casse, ce qui est indispensable pour les noms d'hôte et les chemins Windows. [QSA] conserve la chaîne de requête existante en la concaténant à la nouvelle URL, ce qui est vital quand on ajoute des paramètres. [NE] empêche l'échappement des caractères spéciaux dans la redirection, utile pour les URL contenant des ancres. Ces trois drapeaux sont les plus oubliés, et leur absence produit des comportements difficiles à comprendre. Ajoutez-les dès que vous en avez besoin, c'est gratuit.

[END], [F], [G], [P] : les cas particuliers

[END] arrête complètement le cycle, y compris dans un .htaccess. [F] renvoie une erreur 403 Forbidden, utile pour bloquer des requêtes malveillantes. [G] renvoie une erreur 410 Gone, ce qui indique au client qu'une ressource a définitivement disparu, très utile pour les pages supprimées. [P] force le passage par mod_proxy, ce qui permet de rediriger vers un backend sans que le client ne voie l'URL interne. Ces drapeaux sont moins courants, mais dans certains cas, ils sont exactement ce qu'il faut.

Les pièges classiques qui font échouer une règle

Il y a des erreurs qui reviennent sans cesse, à tel point qu'on peut presque les deviner avant de lire la règle. Les connaître aide à diagnostiquer très vite. Et quand on a déjà vécu le problème une fois, on le reconnaît immédiatement.

Les boucles infinies et le message « too many redirects »

Une règle qui redirige vers une URL qui elle-même déclenche la règle produit une boucle infinie. Le navigateur reçoit alors trop de redirections et affiche une erreur. La cause est presque toujours une règle trop large, comme RewriteRule .* https://example.com [R=301,L], qui correspond à toutes les URL, y compris celle vers laquelle elle redirige. La solution consiste à préciser le motif, ou à ajouter une condition qui exclut l'URL cible. C'est un grand classique, et tout le monde y passe un jour.

L'ordre des règles et l'héritage entre blocs

Les règles s'exécutent dans l'ordre du fichier. Si une règle générale précède une règle spécifique, la générale s'applique et la spécifique n'est jamais atteinte. Mettez les règles les plus spécifiques d'abord. En cas de conflit entre un VirtualHost et un .htaccess, les règles du VirtualHost s'appliquent d'abord. C'est une logique cohérente, mais qui demande de la rigueur. Une règle placée au mauvais endroit produit parfois zéro effet, parfois un effet inattendu.

La différence entre chemin local et URL complète

Dans un VirtualHost, le motif commence par un slash et porte sur l'URL complète. Dans un .htaccess, le motif commence sans slash et porte sur le chemin relatif. Cette différence est cruciale et provoque de nombreuses règles qui fonctionnent dans un contexte et pas dans l'autre. Si vous déplacez une règle d'un fichier à un autre, vérifiez toujours le slash initial. C'est la base, mais c'est aussi la cause d'erreur la plus fréquente.

Le slash final qui ne veut pas rester

Apache peut ajouter ou retirer un slash final en fonction de la configuration. Une règle qui exige un slash final peut ne pas fonctionner si DirectorySlash est mal configuré. Et inversement, une règle qui retire le slash final peut être défaite par une redirection automatique. Vérifiez cette directive et testez le comportement avec curl, en observant les en-têtes. C'est un détail, mais un détail qui casse vite une règle qui semblait correcte.

Exemples concrets et testés

Les exemples qui suivent sont ceux que j'utilise le plus souvent. Ils sont testés en 2.4 et fonctionnent dans la plupart des configurations. Adaptez bien sûr les noms de domaine et les chemins.

Rediriger HTTP vers HTTPS sur un VirtualHost

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    RewriteEngine On
    RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
</VirtualHost>

Cette règle redirige toutes les requêtes vers HTTPS, en conservant le chemin. Simple, efficace. Le motif ^ correspond à toutes les URL, et la substitution inclut le chemin complet. Le drapeau [R=301,L] indique une redirection permanente et un arrêt. Un petit détail : cette règle renvoie vers https://example.com sans le www, ce qui impose un domaine canonique.

Forcer le www ou le non-www

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L]

Cette règle force la version avec www. Inversement, pour forcer le non-www, comparez %{HTTP_HOST} à ^www\. et redirigez vers https://example.com. Pensez au drapeau [NC] pour la casse, et à échapper les points dans l'expression régulière. Sans échappement, example.com correspond aussi à examplexcom, ce qui n'est pas souhaitable.

Réécrire des URL propres vers index.php

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]

C'est la règle classique des framework PHP. Elle renvoie vers index.php si la ressource demandée n'existe pas, ce qui permet de gérer les URL propres côté application. RewriteBase / évite des problèmes de chemin. Le drapeau [L] arrête le traitement. Sans les deux conditions, vous risquez de casser l'accès aux fichiers statiques, ce qui rend le site inutilisable.

Bloquer des user-agents et des hotlinks

RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (bot|spider|crawler) [NC]
RewriteRule ^ - [F]

Cette règle bloque les robots indésirables, en renvoyant une 403. On peut affiner en excluant certains bots légitimes, comme Googlebot. Attention toutefois : RewriteCond sur le user-agent doit être utilisée avec parcimonie, certains outils légitimes peuvent en pâtir. Pour le hotlinking, une règle sur %{HTTP_REFERER} combinée à une exclusion de vos propres domaines est plus efficace.

Tester et déboguer une règle de réécriture

Une règle non testée est une règle qui joue à la roulette russe. Le débogage de mod_rewrite repose sur deux piliers : les logs et la méthode. Les logs disent ce qui se passe, la méthode dit comment chercher. Ensemble, ils font gagner des heures.

LogLevel alert rewrite:trace3 et les logs

Activez un niveau de log élevé pour mod_rewrite avec LogLevel alert rewrite:trace3, ou LogLevel warn rewrite:trace3 selon la configuration. Vous verrez les motifs testés, les résultats, les substitutions appliquées. C'est verbeux, mais extrêmement éclairant. Désactivez-le après le débogage, sinon les logs deviennent énormes. En production, ne laissez jamais trace3 actif sans raison, c'est un coup à remplir le disque.

L'outil en ligne de test et la prudence nécessaire

Certains sites proposent de tester une règle de réécriture en ligne. Utile pour vérifier une expression, mais dangereux pour la confidentialité : ne collez jamais une configuration complète sans réfléchir. Et surtout, ces outils ne simulent pas forcément les contextes réels (VirtualHost, .htaccess, héritage). Considérez-les comme un premier filtre, jamais comme une validation. La vraie validation se fait sur un serveur de test.

Méthode pas à pas pour isoler une règle fautive

Ma méthode est simple : je désactive toutes les règles, je les réactive une par une, je teste après chaque activation, et je regarde les logs. C'est lent, mais c'est systématique. Une règle fautive se trahit immédiatement. Et si deux règles interagissent, je vois tout de suite laquelle pose problème. Cette méthode m'a sauvé des soirées entières, et elle fonctionne quel que soit le contexte.

Performances et bonnes pratiques

La dernière chose à considérer, c'est la performance. Une configuration bien écrite n'a pas d'impact significatif. Une configuration mal écrite peut ralentir un site. La nuance est importante, et quelques bonnes pratiques suffisent à garder un serveur vif.

RewriteRule dans .htaccess ou dans VirtualHost : que choisir ?

Les règles dans un .htaccess sont relues à chaque requête, ce qui a un coût. Les règles dans un VirtualHost sont lues une fois au démarrage ou au rechargement. Si vous contrôlez la configuration Apache, mettez les règles dans le VirtualHost. Vous gagnerez en performance et en lisibilité. Si vous êtes en hébergement mutualisé, vous n'avez pas le choix, mais sachez que le coût existe.

Éviter les règles inutiles et les regex gloutonnes

Chaque règle s'applique à toutes les requêtes qui passent par l'engine. Une regex trop large peut correspondre à des URL non voulues et déclencher un traitement inutile. Une regex gloutonne, comme .*, peut capturer plus que prévu et provoquer des substitutions erronées. Ancrez vos motifs avec ^ et $, évitez les quantificateurs flous, et testez. Un peu de rigueur ici évite bien des problèmes.

FAQ : les questions les plus fréquentes sur RewriteRule

Peut-on tester une RewriteRule sans la mettre en production ? Oui, en utilisant un environnement de staging ou un domaine de test. Activez les logs trace3, vérifiez les résultats, puis désactivez. Ne testez jamais directement en production pour une règle de redirection permanente.

Pourquoi ma règle fonctionne en local et pas sur le serveur ? Les causes possibles : version d'Apache différente, contexte différent (VirtualHost contre .htaccess), chemin de substitution différent, module mod_rewrite non activé, ou configuration de AllowOverride qui bloque les règles dans .htaccess. Vérifiez chacun de ces points.

RewriteRule contre Redirect : quelle différence ? Redirect renvoie une redirection HTTP externe au client. RewriteRule peut faire une réécriture interne, invisible pour le client. Pour une redirection simple, Redirect suffit souvent et se relit plus facilement. Pour une transformation d'URL complexe, RewriteRule est plus adapté.

Conclusion : la réécriture, un outil à apprivoiser, pas à craindre

mod_rewrite a une réputation de bête noire, et c'est injuste. Avec un peu de méthode, une compréhension du moteur et des drapeaux essentiels, on écrit des règles qui fonctionnent, et on les diagnostique rapidement. Les erreurs classiques sont connues, les outils existent, et la documentation officielle, bien que dense, est précise. En 2026, avec des sites toujours plus complexes et des migrations fréquentes, cette compétence vaut de l'or. Prenez le temps d'expérimenter sur un environnement de test, lisez les logs, et surtout : testez avant de basculer en production. C'est le meilleur conseil que je puisse vous donner. Et si vous êtes bloqué, revenez à la base : motif, substitution, contexte, drapeaux. Dans 90 % des cas, le problème est là.

← molitor.photography