Rewrite Guide de réécriture des URLs

Ce document complète la documentation de référence du module mod_rewrite. Il décrit de quelle manière on peut utiliser le module Apache mod_rewrite pour résoudre les problèmes typiques relatifs aux URLs auxquels les webmasters sont souvent confrontés. La résolution de chaque problème par la configuration des jeux de règles de réécriture d'URLs fait l'objet d'une description détaillée.

ATTENTION : l'adaptation des exemples à votre situation en fonction de la configuration de votre serveur pourra s'avérer nécessaire, par exemple l'ajout du drapeau [PT] si vous utilisez les modules mod_alias, mod_userdir, etc... Un jeu de règles défini dans le contexte du serveur devra aussi être adapté pour être utilisé dans un contexte .htaccess. Efforcez-vous toujours de bien comprendre l'effet produit par un jeu de règles avant de l'utiliser, ce qui pourra vous éviter bien des problèmes.
Documentation du module Introduction à mod_rewrite Guide de réécriture avancé - exemples utiles avancés Détails techniques
URLs canoniques
Description :

Sur certains serveurs web, une ressource peut être accessible depuis plusieurs URLs. On trouve en général des URLs canoniques (qui sont réellement utilisables et distribuables), mais aussi des URLs à usage interne, ou celles qui ne sont que des raccourcis, etc... On souhaite que, quelle que soit l'URL que l'utilisateur a fournie avec sa requête, il ne doit en voir en fin de compte que la forme canonique.

Solution :

On effectue une redirection HTTP externe pour toutes les URLs non canoniques afin de les rendre compréhensibles au navigateur et ceci pour toutes les requêtes sous-jacentes. Dans l'exemple de jeux de règles ci-dessous, /~user est remplacé par l'expression canonique /u/user, et une éventuelle absence du slash à la fin de /u/user est corrigée.

RewriteRule   ^/~([^/]+)/?(.*)    /u/$1/$2  [R]
RewriteRule   ^/u/([^/]+)$  /$1/$2/   [R]
Noms d'hôtes canoniques
Description :
Le but de cette règle est de préférer l'utilisation d'un nom d'hôte particulier par rapport à d'autres noms d'hôte utilisables pour atteindre le même site. Par exemple, si vous voulez utiliser www.example.com à la place de example.com, vous devez utiliser une solution de ce style.
Solution :

Pour les sites écoutant sur un port autre que 80:

RewriteCond %{HTTP_HOST}   !^www\.exemple\.com [NC]
RewriteCond %{HTTP_HOST}   !^$
RewriteCond %{SERVER_PORT} !^80$
RewriteRule ^/?(.*)         http://www.example.com:%{SERVER_PORT}/$1
[L,R,NE]

Et pour un site écoutant sur le port 80

RewriteCond %{HTTP_HOST}   !^www\.exemple\.com [NC]
RewriteCond %{HTTP_HOST}   !^$
RewriteRule ^/?(.*)         http://www.example.com/$1 [L,R,NE]

Si vous souhaitez que cette règle s'applique à tous les noms de domaine - en d'autres termes, si vous voulez rediriger example.com vers www.example.com pour toutes les valeurs possibles de example.com, vous pouvez utiliser le jeu de règles suivants :

RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/?(.*) http://www.%{HTTP_HOST}/$1 [L,R,NE]

Vous pouvez utiliser ce jeu de règles aussi bien dans le fichier de configuration de votre serveur principal que dans un fichier .htaccess placé dans le répertoire défini par la directive DocumentRoot du serveur.

<code>DocumentRoot</code>déplacé
Description :

En général, la directive DocumentRoot correspond directement à l'URL "/" du serveur web. Mais souvent, les données qui s'y trouvent ne sont pas de la première priorité. Par exemple, il peut être intéressant, pour les visiteurs qui entrent sur le site pour la première fois, d'être redirigés vers un sous-répertoire particulier /a-propos-de/. Pour ce faire, on peut utiliser le jeu de règles suivant :

Solution :

On redirige l'URL / vers /a-propos-de/:

RewriteEngine on
RewriteRule   ^/$  /a-propos-de/  [R]

Notez que le même effet peut être obtenu à l'aide de la directive RedirectMatch :

RedirectMatch ^/$ http://example.com/apropos/

Notez aussi que cet exemple ne réécrit que l'URL racine. En d'autres termes, il réécrit une requête pour http://example.com/, mais ne réécrira pas une requête pour http://example.com/page.html. En fait, si vous avez modifié la racine de vos documents - c'est à dire si tous vos contenus se trouvent dans ce sous-répertoire, il vaut mieux simplement modifier votre directive DocumentRoot que de procéder à une réécriture d'URLs.

Problème du slash de fin
Description :

La plupart des problèmes de "slash de fin" peuvent être résolus grâce aux techniques décrites dans ce sujet de la FAQ. Cependant, dans certaines situations où l'absence de slash de fin peut rendre une URL inopérante, l'utilisation de mod_rewrite s'avère nécessaire. Le cas peut se présenter, par exemple, après une série complexe de règles de réécriture.

Solution :

La solution à ce problème subtil consiste à laisser le serveur ajouter le slash de fin automatiquement. Pour y parvenir, il faut utiliser une redirection externe, afin que le navigateur demande correctement les images sous-jacentes, etc... Une réécriture en interne ne fonctionnerait que pour la page du répertoire, mais échouerait pour toute image incluse dans cette page via des liens relatifs, car le navigateur demanderait un objet inséré. Par exemple, une requête pour image.gif dans /~quux/foo/index.html deviendrait /~quux/image.gif sans la redirection externe !

Pour y parvenir, on peut utiliser des règles de ce style :

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo$  foo/  [R]

Vous pouvez aussi ajouter ce qui suit dans un fichier .htaccess situé dans le répertoire contenant la ressource. Notez cependant que cela augmente la charge du processeur.

RewriteEngine  on
RewriteBase    /~quux/
RewriteCond    %{REQUEST_FILENAME}  -d
RewriteRule    ^(.+[^/])$           $1/  [R]
Déplacement des répertoires home vers un autre serveur
Description :

De nombreux webmasters ont demandé comment résoudre le problème suivant : ils voudraient tout simplement rediriger les répertoires home d'un serveur web vers un autre serveur web. Cette situation se présente en général lorsqu'on installe un nouveau serveur web destiné à terme à en remplacer un autre plus ancien.

Solution :

Avec mod_rewrite, la solution est évidente. Sur l'ancien serveur web, on redirige simplement toutes les URLs du style /~user/chemin vers http://nouveau-serveur/~user/chemin.

RewriteEngine on
RewriteRule   ^/~(.+)  http://nouveau-serveur/~$1  [R,L]
Recherche de pages dans plus d'un répertoire
Description :

Le serveur web doit parfois rechercher des pages dans plus d'un répertoire. Dans ce cas, les vues multiples ou autres techniques similaires ne sont d'aucun secours.

Solution :

On définit explicitement un jeu de règles qui recherche les fichiers dans les répertoires.

RewriteEngine on

#   on cherche tout d'abord dans dir1/...
#   ... et si on trouve, on est content et on arrête :
RewriteCond         %{DOCUMENT_ROOT}/dir1/%{REQUEST_URI}  -f
RewriteRule  ^(.+)  %{DOCUMENT_ROOT}/dir1/$1  [L]

#   on cherche ensuite dans dir2/...
#   ... et si on trouve, on est content et on arrête :
RewriteCond         %{DOCUMENT_ROOT}/dir2/%{REQUEST_URI}  -f
RewriteRule  ^(.+)  %{DOCUMENT_ROOT}/dir2/$1  [L]

#   sinon, on continue la recherche avec d'autres directives Alias
#   ou ScriptAlias, etc...
RewriteRule   ^(.+)  -  [PT]
Définir des variables d'environnement en fonction de certaines parties de l'URL
Description :

Comment conserver des informations d'état d'une requête à l'autre et utiliser l'URL pour les encoder, sans utiliser d'encapsulateur CGI pour toutes les pages pour seulement supprimer ces informations.

Solution :

On utilise une règle de réécriture pour supprimer l'information d'état et l'enregistrer dans une variable d'environnement dont on pourra plus tard extraire la valeur dans XSSI ou CGI. De cette façon, une URL telle que /foo/S=java/bar/ sera traduite en /foo/bar/ et la variable d'environnement STATUS aura pour valeur "java".

RewriteEngine on
RewriteRule   ^(.*)/S=([^/]+)/(.*)    $1/$3 [E=STATUS:$2]
Hôtes virtuels basés sur l'utilisateur
Description :

Supposons que vous vouliez atteindre la page d'accueil des utilisateurs sur une même machine au moyen de l'URL www.nom-utilisateur.hôte.domaine.com, en vous basant seulement sur les enregistrements DNS de type A, et ceci sans qu'aucun hôte virtuel ne soit installé sur cette machine.

Solution :

Dans le cas des requêtes HTTP/1.0, il n'y a pas de solution ; par contre, avec une requête HTTP/1.1 qui contient un en-tête HTTP Host:, on peut utiliser le jeu de règles suivant pour réécrire en interne http://www.nom-utilisateur.hôte.com/chemin vers /home/nom-utilisateur/chemin :

RewriteEngine on
RewriteCond   %{HTTP_HOST}                 ^www\.([^.]+)\.host\.com$
RewriteRule   ^(.*) /home/%1$1

Les parenthèses utilisées dans une directive RewriteCond sont capturées dans les références arrières %1, %2, etc..., alors que les parenthèses utilisées dans une directive RewriteRule sont capturées dans les références arrières $1, $2, etc...

Redirection des répertoires d'accueil pour les étrangers
Description :

On veut rediriger les URLs des répertoires d'accueil vers un autre serveur www.quelque-part.com lorsque l'utilisateur demandeur n'appartient pas au domaine local notre-domaine.com. On rencontre parfois cette situation dans un contexte d'hôtes virtuels.

Solution :

Juste une condition de réécriture :

RewriteEngine on
RewriteCond   %{REMOTE_HOST}  !^.+\.notre-domaine\.com$
RewriteRule   ^(/~.+)         http://www.quelque-part.com/$1 [R,L]
Redirection des ancrages
Description :

Par défaut, la redirection vers un ancrage HTML ne fonctionne pas, car mod_rewrite échappe le caractère # en le transformant en %23, ce qui rend la redirection inopérante.

Solution :

On utilise le drapeau [NE] dans la règle RewriteRule. NE signifie "No Escape".

Réécriture dépendant de l'heure
Description :

Lorsqu'il s'agit de distribuer des contenus dont la nature dépend de l'heure, de nombreux webmasters utilisent encore des scripts CGI qui redirigent par exemple vers des pages spécifiques. Comment peut-on y parvenir à tenir compte de l'heure à l'aide de mod_rewrite ?

Solution :

Il existe de nombreuses variables nommées TIME_xxx utilisables dans les conditions de réécriture. Utilisées en conjonction avec les modèles de comparaison lexicographique spéciaux <STRING, >STRING et =STRING, elles permettent d'effectuer des redirections dépendant de l'heure :

RewriteEngine on
RewriteCond   %{TIME_HOUR}%{TIME_MIN} >0700
RewriteCond   %{TIME_HOUR}%{TIME_MIN} <1900
RewriteRule   ^foo\.html$             foo.jour.html
RewriteRule   ^foo\.html$             foo.nuit.html

Avec cet exemple, l'URL foo.html renvoie le contenu de foo.jour.html durant le créneau horaire 07:00-19:00, et le contenu de foo.nuit.html le reste du temps. Agréable fonctionnalité pour une page d'accueil...

Compatibilité ascendante pour une migration de YYYY vers XXXX
Description :

Comment conférer une compatibilité ascendante aux URLs (existant encore virtuellement) après avoir migré document.YYYY vers document.XXXX, c'est à dire après avoir par exemple traduit un lot de fichiers .html en fichiers .phtml ?

Solution :

On réécrit simplement le nom du fichier en son nom de base et vérifie s'il existe aussi avec la nouvelle extension. Si c'est le cas, on utilise ce nom, sinon on réécrit l'URL sous sa forme originale.

#   jeu de règles assurant une compatibilité ascendante en réécrivant
#   document.html en document.phtml si et seulement si document.phtml
#   existe et document.html n'existe plus
RewriteEngine on
RewriteBase   /~quux/
#   réécriture du fichier en son nom de base,
#   mais garde en mémoire le fait qu'il s'agit
#   d'un fichier html
RewriteRule   ^(.*)\.html$              $1      [C,E=WasHTML:yes]
#   réécrit vers document.phtml s'il existe
#   Note : il s'agit d'un exemple de niveau répertoire, si bien que
#   %{REQUEST_FILENAME} contient le chemin complet du système de fichier
#   tel qu'il a été construit par le serveur.
RewriteCond   %{REQUEST_FILENAME}.phtml -f
RewriteRule   ^(.*)$ $1.phtml                   [S=1]
#   sinon, restauration du nom de fichier complet original
RewriteCond   %{ENV:WasHTML}            ^yes$
RewriteRule   ^(.*)$ $1.html
De l'ancien au nouveau (en interne)
Description :

Supposons que nous ayons récemment renommé la page foo.html en bar.html, et voulions maintenant que l'ancienne URL soit toujours valide à des fins de compatibilité ascendante. En fait, on voudrait que le changement de nom soit transparent aux utilisateurs de l'ancienne URL.

Solution :

On réécrit l'ancienne URL en interne vers la nouvelle via la règle suivante :

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo\.html$  bar.html
De l'ancien au nouveau (en externe)
Description :

Supposons toujours que nous ayons récemment renommé la page foo.html en bar.html, et voulions maintenant que l'ancienne URL soit toujours valide à des fins de compatibilité ascendante. Par contre, nous voulons cette fois que les utilisateurs de l'ancienne URL soient redirigés vers la nouvelle, c'est à dire que l'adresse tapée dans leur navigateur doit aussi être modifiée.

Solution :

On force une redirection HTTP vers la nouvelle URL, ce qui entraîne une modification de celle du navigateur et aussi de ce que voit l'utilisateur :

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo\.html$  bar.html  [R]
De statique à dynamique
Description :

Comment transformer une page statique foo.html en sa variante dynamique foo.cgi de manière transparente, c'est à dire sans en avertir le navigateur/utilisateur.

Solution :

On réécrit simplement l'URL en script CGI et force le gestionnaire de contenu à cgi-script de façon à ce que le script s'exécute en tant que programme CGI. Ainsi, une requête vers /~quux/foo.html conduit en interne à l'invocation de /~quux/foo.cgi.

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo\.html$  foo.cgi  [H=cgi-script]
Blocage des robots
Description :

Comment empêcher un robot vraiment gênant de collecter les pages d'une partie spécifique du site web ? Un fichier /robots.txt comportant les entrées du "Protocole d'Exclusion des Robots" ne suffit généralement pas à en venir à bout.

Solution :

On utilise un jeu de règles qui interdit les URLs de la partie du site web concernée /~quux/foo/arc/ (peut-être une partie du serveur avec une arborescence très développée à travers laquelle le parcours du robot induirait une charge importante pour le serveur). Nous devons nous assurer de n'interdire l'accès qu'à ce robot particulier, c'est à dire qu'il ne suffit pas d'interdire l'accès à l'hôte sur lequel le robot fonctionne, ce qui bloquerait aussi les utilisateurs de cet hôte. Pour y parvenir, on tient aussi compte des informations contenues dans l'en-tête HTTP User-Agent.

RewriteCond %{HTTP_USER_AGENT}   ^NameOfBadRobot.*
RewriteCond %{REMOTE_ADDR}       ^123\.45\.67\.[8-9]$
RewriteRule ^/~quux/foo/arc/.+   -   [F]
Blocage du référencement à chaud (Hotlinking) d'images
Description :

Cette technique vous permet d'interdire à d'autres sites d'inclure directement vos images dans leurs pages. On fait souvent référence à cette pratique sous le nom de référencement à chaud (Hotlinking) qui entraîne l'utilisation de votre bande passante pour servir des contenus faisant partie du site de quelqu'un d'autre.

Solution :

Cette technique repose sur la valeur de la variable optionnelle HTTP_REFERER. Certaines personnes pourront donc contourner cette limitation. Pour la plupart des utilisateurs cependant, la requête échouera, en ce sens que l'image ne sera pas affichée depuis le site tiers.

Il y a plusieurs manières de gérer cette situation.

Dans le premier exemple, nous rejetons tout simplement la requête si elle ne provenait pas d'une page appartenant à notre site. Pour les besoins de cet exemple, nous supposons que le nom de votre site est www.example.com.

RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !www.example.com [NC]
RewriteRule \.(gif|jpg|png)$    -   [F,NC]

Dans le second exemple, plutôt que de rejeter la requête, nous affichons une autre image à la place.

RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !www.example.com [NC]
RewriteRule \.(gif|jpg|png)$    /images/go-away.png   [R,NC]

Dans le troisième exemple, nous redirigeons la requête vers une image appartenant à un site tiers.

RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !www.example.com [NC]
RewriteRule \.(gif|jpg|png)$ http://other.site.com/image.gif   [R,NC]

De tous ces exemples, les deux derniers semblent les plus efficaces pour faire en sorte que les gens arrêtent de référencer vos images à chaud, car il ne verront pas les images qu'ils s'attendent à voir.

Interdiction du mandataire
Description :

Comment interdire l'utilisation du mandataire d'Apache à un certain hôte, ou même à un utilisateur d'un certain hôte ?

Solution :

Nous devons tout d'abord nous assurer que mod_rewrite se situe en dessous (!) de mod_proxy dans le fichier de configuration lors de la compilation du serveur web Apache. De cette façon, il est appelé avant mod_proxy. Nous pouvons alors utiliser la règle suivante pour une interdiction concernant un hôte...

RewriteCond %{REMOTE_HOST} ^mauvais-hôte\.mon-domaine\.com$
RewriteRule !^http://[^/.]\.mon-domaine.com.*  - [F]

...et celle-ci pour une interdiction concernant un utilisateur d'un certain hôte :

RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST}
^mauvais-sujet@mauvais-hôte\.mon-domaine\.com$
RewriteRule !^http://[^/.]\.mon-domaine.com.*  - [F]
Moteur de réécriture externe
Description :

Une question de la Faq : comment résoudre le problème FOO/BAR/QUUX/etc. ? mod_rewrite ne semble pas devoir y apporter de solution...

Solution :

Utiliser une RewriteMap ou table de réécriture externe, c'est à dire un programme qui agit de la même façon qu'une RewriteMap. Il doit être lancé une fois au démarrage d'Apache, recevoir les URLs des requêtes sur STDIN, et restituer l'URL résultante (en général réécrite) sur STDOUT (dans cet ordre !).

RewriteEngine on
RewriteMap    quux-table       prg:/chemin/vers/table.quux.pl
RewriteRule   ^/~quux/(.*)$  /~quux/${quux-table:$1}
#!/chemin/vers/perl

#   désactive la mise en tampon des entrées/sorties, qui risque
#   de provoquer des bouclages infinis pour le serveur Apache
$| = 1;

#   lit les URLs (une par ligne) depuis stdin et
#   génère l'URL transformée sur stdout

#   read URLs one per line from stdin and
#   generate substitution URL on stdout
while (<>) {
    s|^foo/|bar/|;
    print $_;
}

Ceci n'est qu'un exemple de démonstration qui ne fait que réécrire les URLs du style /~quux/foo/... vers /~quux/bar/.... En fait, vous pouvez programmer la substitution que vous voulez. Notez cependant que si de tels programmes peuvent aussi être utilisés par un utilisateur standard, seul l'administrateur du système peut les écrire.