Apache et PHP : La réécriture d'URL

f-demu01

Ce tutoriel a pour but de vous donner quelques méthodes pour effectuer la réécriture de liens avec PHP et Apache. ♪

- Titre : Apache et PHP : La réécriture d'URL

- Auteur : f-demu01

- Parution : 23 octobre 2003

- Mise à jour : 4 mai 2009

- Licence : Copyright ® 2003 f-demu01. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Introduction

- 1. Pourquoi réécrire ses liens ?

- 2. Généralités

- 3. La directive ErrorDocument

- 4. L'option FilesMatch

- 5. Le module mod_rewrite d'Apache

- 6. Conclusion

Introduction

L'utilisation de langages haut niveau, à l'instar de PHP, pour la construction de sites Web, ainsi que l'interaction avec des bases de données forcent aujourd'hui le webmaster à assaisonner tous ses liens d'une multitude de paramètres de configuration, d'exécution, de mise en page, et j'en passe. On est rapidement amené à produire des liens d'une longueur astronomique et d'une complexité rare.
Le serveur Apache offre plusieurs moyens pour transformer ses liens et les rendre plus lisibles, et augmenter la sécurité liée à l'apparition du nom des paramètres dans l'URL.
Merci à Armatatuxa, Jérôme pour les relectures, ainsi qu'à Henri Cesbron Lavau pour la précision concernant la génération de caches.

1 - Pourquoi réécrire ses liens ?

Tout d'abord, par souci de clarté. Certains internautes (moi le premier) n'apprécient guère les URL incompréhensibles et à rallonge dans la barre d'adresses du navigateur. Une URL « formatée » sera beaucoup plus facile à retenir que ce type de lien. Certes, il est possible de n'afficher que le DNS quelle que soit la page demandée, mais cela diminue la pertinence des robots des moteurs de recherche.

http://www.monsite.com/message/index.php?page=01&cat=10&langue=02

Il est également plus intéressant de produire des liens masqués en HTML plutôt que des liens composés pour améliorer la qualité de votre référencement. En effet, certains robots sont incapables d'interpréter les liens composés. Si la proportion de ces robots s'amenuise avec le temps, il en existe encore suffisamment pour qu'on les prenne en compte.

Enfin, la raison la plus flagrante de la mise en place d'un tel système est la sécurité. Lorsque vous utilisez des liens composés, on voit apparaître des valeurs, mais surtout des noms de variable, qui sont autant de portes dérobées pour entrer dans votre site. Prenons un exemple extrême pour l'illustrer: un script qui permettrait de visualiser le source d'un fichier du serveur en passant celui-ci en paramètre http://www.monsite.com/index.php?fichier=test.txt pourrait être détourné afin de récupérer un fichier système :

http://www.monsite.com/index.php?fichier=/etc/fstab

Bien sûr, cet exemple est un cas extrême et simpliste que je vous invite à ne jamais reproduire.

Quand bien même la liste n'est pas exhaustive, voilà plusieurs (bonnes) raisons pour lesquelles il devient de plus en plus important d'utiliser ce type de sécurité.

2 - Généralités

Pour chaque méthode présentée, on utilisera l'exemple d'un site Web contenant plusieurs rubriques, chaque d'elles contenant plusieurs pages, toutes ces pages étant accessibles en plusieurs langues, en mode navigation (normal) ou impression. Par souci de cohésion, la traduction des adresses se fera en PHP, cela dit il est possible d'utiliser d'autres langages. Les liens standards ont cette forme :

http://www.monsite.com/index.php?section=1&page=2&langue=fr&print=no

Lorsque vous utilisez l'URL rewriting, tous les liens que vous créerez sur votre site devront suivre un patron commun. Voici le schéma du nom des pages que nous voulons obtenir.

http://www.monsite.com/rubrique_page_langue_imprimer.html.

Le but de l'URL rewriting est donc d'intercepter côté serveur l'adresse de la page demandée, d'en extraire les paramètres, et de renvoyer côté client la page correspondante (qui n'a pas d'équivalent physique sur le serveur).

3 - La directive ErrorDocument

Cette méthode, la plus simple, utilise la capacité d'Apache à rediriger vers une même page tous les documents introuvables (plus connue sous le nom d'erreur 404). Créez ou éditez le fichier .htaccess à la racine de votre serveur, et ajoutez-y la ligne suivante :

ErrorDocument 404 /error.php

Si votre site ne se situe pas à la racine du site Web, il faut mentionner l'arborescence complète ( par exemple ./error.php ne fonctionnera pas).
On utilise ici le fichier error.php comme fichier de redirection afin de séparer la partie de traitement de la partie affichage (gérée par index.php), mais libre à vous de tout centraliser dans le fichier index.php. Il ne reste plus qu'à remplir le fichier error.php pour le traitement des paramètres.

## On isole le nom du fichier
$fic_tmp = explode( "/", $_SERVER['REQUEST_URI'] );
$fic_demande = array_pop($fic_tmp);

## On enlève son extension (s'il y en a une)
$parametres_tmp = explode( ".", $fic_demande );

## On répartit dans un tableau les différents paramètres
$parametres = explode( "_", $parametres_tmp[0] );

$section = $parametres[0];
$page = $parametres[1];
$langue = $parametres[2];
$imprim = $parametres[3];

## Inclusion du fichier index.php, qui utilise les paramètres
include_once("/var/www/index.php");

Notes
Cette méthode m'a posé des problèmes pour l'utilisation des formulaires avec la méthode POST: lors de l'envoi, la page se rechargeait sans raison apparente.
Si cette méthode ne fonctionne pas et que vous êtes certains d'avoir suivi toutes les étapes, poussez la taille de votre fichier de traitement (ici, error.php) au-delà de 512 octets, en y ajoutant des commentaires par exemple: il s'agirait apparemment d'un bug lié à l'utilisation d'Internet Explorer.
Attention lorsque vous utilisez cette méthode: le fait d'interpréter chaque page comme étant un document inconnu augmente la taille de vos fichiers de log access.log et error.log.

4 - L'option FilesMatch

Afin d'interdire l'accès à certains documents au sein d'un répertoire sous Apache, il est très fréquent de faire appel à la directive FilesMatch. Cette directive permet également d'adopter un comportement selon le « type » de fichier rencontré. Nous allons ici l'utiliser pour « diriger » les fichiers. Cette méthode est un peu différente des deux autres, car elle ajoute obligatoirement un niveau dans l'arborescence du site.

La première étape est de créer un fichier .htaccess à la racine du répertoire pour lequel on veut activer cette fonctionnalité. On y ajoute les lignes suivantes :

<FilesMatch "^(html)$">
ForceType application/x-httpd-php
<FilesMatch>

Cela peut se traduire ainsi: pour tout fichier dont le nom contient la chaîne exacte « html », son type MIME est forcé à http-php. En pratique, tous les fichiers contenus dans la sous-arborescence /html/ seront interprétés comme étant du PHP. J'attire votre attention sur l'importance de ne travailler qu'avec des liens absolus, l'utilisation de liens relatifs (déconseillée de toute façon;) ) peut amener à des incohérences.
Considérons par exemple le script suivant

function affich_bouton() {
echo '<IMG SRC="./images/bouton.gif" alt="bouton" />';
}

On utilise la fonction affich_bouton dans le fichier index.php. Lorsque l'on arrive sur le site, aucun problème, puisque l'URL est http://www.monsite.com/ . À présent, cliquons sur un lien mettant en œuvre une URL réécrite. Celle-ci est de la forme www.monsite.com/html/01_fr.html, et le script index.php utilise donc comme base www.monsite.com/html/ et pour lui, le chemin de l'image devient donc www.monsite.com/html/images/bouton.gif.

Comme pour la première méthode utilisée, il faut à présent définir le traitement des pages concernées par la règle sus-citée. A cet effet, il faut créer au même niveau que .htaccess, un fichier nommé html, dont le contenu sera le même que celui de « error.php » abordé au premier chapitre.

## On isole le nom du fichier
$fic_tmp = explode( "/", $_SERVER['REQUEST_URI'] );
$fic_demande = array_pop($fic_tmp);

## On enlève son extension (s'il y en a une)
$parametres_tmp = explode( ".", $fic_demande );

## On répartit dans un tableau les différents paramètres
$parametres = explode( "_", $parametres_tmp[0] );

$section = $parametres[0];
$page = $parametres[1];
$langue = $parametres[2];
$imprim = $parametres[3];

## Inclusion du fichier index.php, qui utilise les paramètres
include_once( "/var/www/index.php" );

5 - Le module mod_rewrite d'Apache

Le module mod_rewrite est un module standard d'Apache qui permet de manipuler les URL facilement: Brian Moore, auteur de la documentation Apache à ce sujet, le décrit comme « le couteau suisse de la manipulation d'URL ».
Tout d'abord, sachez que le module mod_rewrite d'Apache ne fonctionne pas avec les versions inférieures à Apache 1.3.27, ce qui signifie entre autres que les utilisateurs de EasyPHP 1.6, rencontreront des problèmes. Si votre version d'Apache supporte l'URL Rewriting, la question se pose quant à sa disponibilité chez votre hébergeur: en effet, beaucoup le désactivent pour des raisons de sécurité.
Une connaissance de base des expressions régulières sera également un plus non négligeable, je vous renvoie donc à cet effet au tutoriel regex de Guillaume Rossolini.

La première chose à faire est l'activation, si vous travaillez en local, ou si vous disposez d'un serveur dédié, du module mod rewrite: éditez votre fichier httpd.conf, et ajoutez (ou, à défaut, décommentez) les deux lignes suivantes :

LoadModule rewrite_module modules/mod_rewrite.so
AddModule mod_rewrite.c # Pour les versions 1.2 et 1.3 d'Apache uniquement et en présence de la directive ClearModuleList

Voilà qui vous permet d'utiliser les règles de réécriture d'URL.
À présent, toujours en reprenant l'exemple de site de départ, nous allons créer ou modifier le fichier .htaccess et définir la règle de réécriture.

# fichier .htaccess
Options FollowSymLinks
RewriteEngine on
RewriteRule ^([0-9]{2})_([0-9]{2})_([a-z]{2})_([a-z])\.html$ index.php?section=$1&page=$2&langue=$3&imprim=$4 [L]

Intéressons-nous aux quelques lignes que nous venons d'écrire dans ce fichier: la première est une option à activer, pour des raisons de sécurité. Si l'option n'est pas active chez votre hébergeur, l'URL Rewriting peut ne pas fonctionner sur votre site.
La seconde ligne active le moteur de réécriture d'URL, lequel est désactivé par défaut. Cette ligne est importante, car elle vous permettra, si vous utilisez plusieurs règles de réécriture, de toutes les ignorer en passant le paramètre à Off, plutôt que de commenter toutes les règles une par une. Cette commande s'utilise une seule fois dans le fichier .htaccess, quel que soit le nombre de règles utilisées.
Enfin, la troisième ligne est la règle, qu'il faut décortiquer pour bien comprendre. Celle-ci comporte trois blocs: l'instruction RewriteRule, l'URL à réécrire, et l'URL réécrite.

Si on s'intéresse au motif, on peut le décomposer comme suit: on trouve quatre groupes entourés de parenthèses, séparés par des « _ ». Pour comprendre en détail ces règles, utilisez un tutoriel sur les expressions régulières, sinon, voici brièvement leur signification :

- groupe 1 : ensemble de deux caractères compris entre 0 et 9 (section) ;

- groupe 2 : idem groupe 1 (page) ;

- groupe 3 : ensemble de deux lettres minuscules (langue) ;

- groupe 4 : une lettre minuscule (imprim).

Voici donc nos quatre paramètres. Petite précision, le fait de les entourer de parenthèse les sauvegarde dans des variables, dans leur ordre d'arrivée ($1, $2…$0 étant le nom de la page demandée).

Regardons à présent l'URL réécrite :

index.php?section=$1&page=$2&langue=$3&imprim=$4

Celle-ci est en tout point commune avec une URL standard, mais au lieu de valeurs fixes pour les paramètres, on y trouve les variables créées juste avant grâce aux parenthèses. Il ne vous reste plus qu'à récupérer dans votre fichier index.php les valeurs des paramètres, et de les utiliser à votre convenance.

À la fin de la ligne subsiste une directive: [L]. Il existe une liste d'options qu'il est possible de combiner pour chaque règle. Celle que nous utilisons ici permet de spécifier que cette règle sera la dernière appliquée. Dans le cas présent, elle n'est pas nécessaire, mais elle le devient lorsque l'on travaille avec plusieurs motifs.
Autre utilisation de la règle last, la création et la gestion de caches. Le principe est de vérifier, pour chaque page demandée, si le fichier cache est présent sur le serveur. Si c'est le cas, c'est cette page qui sera renvoyée à l'internaute. Sinon, la page est créée.

RewriteEngine on
#Si la page.cache existe :
RewriteCond %{REQUEST_FILENAME}\.cache -f
#on réécrit son url:
RewriteRule ([^.]+)\.html$ /$1.html.cache [L]
#et du fait du [L], on se serait arrêté là si elle existait.
#sinon, on exécute le script page.php en passant le nom de la page demandée en paramètre :
RewriteRule ([^.]+)\.html$ /page.php?page=$1 [L]

Dans le cas ci-dessus, on teste l'existence de la page ( l'option -f permet de s'assurer que la ressource est un fichier et non un répertoire). La directive RewriteCond permet d'appliquer une réécriture seulement si la condition spécifiée (ici, %{REQUEST_FILENAME}\.cache -f ) est vérifiée. Étant donné que l'on spécifie [L] en fin de ligne, le moteur n'ira même pas lire les lignes suivantes. En revanche, si la condition n'est pas vérifiée, la ligne RewriteRule ([^.]+)\.html$ /$1.html.cache [L] n'est pas prise en compte, ce qui permet l'exécution de la ligne suivante.

6 - Conclusion

Chacune des méthodes proposées ici permet de faire correspondre à un fichier n'ayant pas d'existence en tant que fichier physique sur un serveur Internet un contenu HTML. Si l'URL rewriting permet de renforcer la sécurité de votre site, cela n'est pas pour autant un gage d'inviolabilité, aussi pensez bien aux autres aspects de la sécurité concernant votre site Internet.