Guide de réécriture des URLs - Serveur Apache HTTP

Serveur Apache HTTP Version 2.3

Apache > Serveur HTTP > Documentation > Version 2.3 > Rewrite

# Guide de réécriture des URLs

Langues Disponibles:

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.

### Voir aussi

- 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.

## `DocumentRoot`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.

Langues Disponibles: