Apprendre .htaccess : URL Rewriting

Le fichier .htaccess permet de réécrire des URLs dans le but de les simplifier et les rendre « SEO friendly ».

Icône de calendrier
Avancé
7 chapitres

Qu’est-ce que l’URL rewriting ?

La réécriture d’URL (ou URL rewriting) est un moyen très efficace d’effectuer des redirections avancées ou simplement de changer l’apparence des URLs.

Les redirections et modifications d’URL sont des points qu’il est important de considérer pour le SEO d’un site web.

Activer le moteur de réécriture

Si le module mod_rewrite est bien activé sur votre serveur, vous pourrez ensuite l’exploiter dans votre fichier 📄 .htaccess en écrivant la directive suivante :

.htaccess
copié !
# Activation du module de réécriture
RewriteEngine on

Bases de l’URL rewriting

Règles de réécriture

Une fois le module de réécriture activé depuis notre 📄 .htaccess, il est possible de définir des règles de réécriture.

Une règle de réécriture se déclare avec la directive suivante :

.htaccess
copié !
RewriteRule <modèle> <substitution> [<drapeaux>]

Les 3 arguments, séparés par des espaces ont un rôle bien distinct :

modèle : le modèle des URLs pour lesquelles la règle doit s’appliquer.

Ce pattern s’écrit sous forme d’expression régulière s’appliquant sur le chemin de l’URL (la partie après le nom de domaine et avant le ? des paramètres d’URL GET)

substitution : en quoi la requête correspondante doit être transformée. Il peut s’agir :

  • D’un chemin (vers un fichier ou correspondant à une route de l’application)
  • D’une URL absolue (avec http:// ou https://)
  • D’un tiret (noté -) indiquant qu’aucune substitution ne doit être effectuée. Ceci est utile quand un drapeau doit être appliqué sans modifier le chemin.

[drapeaux] ou « flags » (optionnel) : options affectant la requête réécrite. Consulter la liste des drapeaux.

.htaccess
copié !
RewriteRule ^demo$ demo.php [NC]
  • Le symbole ^ signifie le début de l’URL, après le nom de domaine (« rien avant »).
  • Le symbole $ signifie la fin de l’URLrien après »).
  • demo est le modèle d’URL pour lequel la règle doit s’appliquer.
  • demo.php est le fichier de substitution associé au modèle.
  • [NC] est le drapeau qui spécifie que la règle n’est pas sensible à la casse.

Le fait d’accéder à http://monsite.com/demo dans le navigateur va pointer vers le fichier demo.php (sans que l’on s’en aperçoive - l’URL ne changera pas). Les 3 URLs suivantes seront alors valides :

  • http://monsite.com/demo : car il s’agit de la règle définie.
  • http://monsite.com/Demo : car la règle n’est pas sensible à la casse.
  • http://monsite.com/demo.php : car le fichier d’origine répondra toujours à la requête normalement.

Conditions de réécriture

Il est possible de spécifier une ou plusieurs condition(s) préalable(s) à l’exécution d’une RewriteRule. Ces conditions sont particulièrement utiles si l’on ne souhaite pas conditionner notre règle de redirection au modèle d’URL seul (comme c’est le cas avec RewriteRule). Cela se fait alors par l’intermédiaire de la directive RewriteCond.

Une condition de réécriture se déclare avec la directive suivante :

.htaccess
copié !
RewriteCond <caracteristique> <condition> [<drapeaux>]

Les 3 arguments, séparés par des espaces ont un rôle bien distinct :

  • caracteristique : une variable serveur (à la forme %{VARIABLE}) décrivant une caractéristique de la requête.
  • condition : une condition sous forme d’expression rationnelle correspondant à la variable serveur.
  • [drapeaux] ou « flags » (optionnel) : options affectant la requête réécrite. Consulter la liste des drapeaux.
.htaccess
copié !
RewriteCond %{HTTP_HOST} !https://monsite.com$
RewriteRule ...

Cet exemple déclenchera la RewriteRule qui suivra uniquement si l’URL de la requête est différente de https://monsite.com.

Références arrières

Comme on utilise des expressions régulières avec RewriteRule et RewriteCond, on va pouvoir capturer des portions de chaînes pour les réutiliser. Cela se fait via l’usage de parenthèses capturantes () autour de notre expression régulière.

Considérons l’extrait htaccess suivant :

.htaccess
copié !
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)$
RewriteRule ^(.*) http://%2/laconsole/htaccess/test/$1?subdomain=%1 [R=301,L,NE]

Ici les parenthèses permettent de capturer l’expression régulière .* et de les placer dans une variable (invisible) à laquelle on pourra faire référence depuis la chaîne de subsitution d’une règle de réécriture. Dans une expression régulière :

  • Un point . représente un caractère quelconque, sauf pour un saut de ligne
  • Une astérisque * signifie « zéro ou plusieurs occurrences »

Cette expression régulière indique donc « une suite de 0 ou plusieurs caractères quelconques ».

Les références arrières de règle $N

Pour N compris entre 1≤N≤9, elles font référence aux parenthèses capturantes du modèle de la rewriteRule.

Les références arrières de condition %N

Pour N compris entre 1≤N≤9, elles font référence aux parenthèses capturantes de la condition de la rewriteCond.

Considérons le 📄 .htaccess suivant :

.htaccess
copié !
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)$
RewriteRule ^(.*) http://%2/path/to/project/$1?subdomain=%1 [R=301,L,NE]

Ce fichier htaccess va réécrire une URL de la forme http://blog.localhost/foo/bar en http://localhost/foo/bar/?subdomain=blog

  • %1 récupère les caractères avant le point . dans le %{HTTP_HOST} (ici : blog)
  • %2 récupère les caractères après le point . dans le %{HTTP_HOST} (ici : localhost)
  • $1 récupère le chemin d’URL depuis la racine du projet (ici : foo/bar/)

Préfixe d’URL

Dans le cas où la requête initiale, ainsi que la substitution (définie dans la RewriteRule), ne se situent pas par rapport au répertoire racine du serveur (DocumentRoot), il est bien souvent important (mais pas systématique - voir documentation) de déclarer un préfixe d’URL grâce à la directive suivante :

copié !
RewriteBase <chemin_racine>

Cette directive permet de spécifier le préfixe d’URL à utiliser pour les RewriteRule qui réécrivent vers un chemin relatif.

Considérons l’architecture de dossiers suivante :

📁 www
 | 📁 site1
 |  | ...
 | 📁 site2
 | 📁 site3

On pointera ainsi vers site 1 avec la directive RewriteBase /site1/public.

Quelques grandes règles de réécriture

Exploitons ici la mécanique de l’URL rewriting pour gérer 3 grands classiques : la redirection HTTP vers HTTPS, la redirection WWW vers le domaine de base ou encore la réécriture des « query strings ».

1. Redirection HTTP vers HTTPS

Il est aujourd’hui primordial pour un site web d’être consulté via le protocole HTTPS. C’est un gage de sécurité pour les utilisateurs (données cryptées) et un point non négligeable en termes de bénéfices SEO.

Lorsqu’un site possède un certificat SSL, il est apte à être consulté via le protocole HTTPS. Pour éviter toute exploitation malencontreuse du protocole HTTP, les développeurs ont alors pris l’habitude de rediriger les URLs HTTP vers HTTPS.

L’extrait suivant permet de rediriger les requêtes HTTP vers HTTPS :

.htaccess
copié !
# Rediriger HTTP vers HTTPS
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]

RewriteEngine On permet d’activer le mode de réécriture.

Une règle de réécriture s’applique uniquement si les conditions spécifiées par RewriteCond sont remplies. RewriteCond %{HTTPS} !=on signifie que la réécriture ne sera effective que si l’URL est demandée sans le protocole HTTPS. En fonction du serveur, il peut être possible de spécifier RewriteCond %{HTTPS} off.

Une règle de réécriture est définie avec l’instruction RewriteRule. RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] réécrit le protocole HTTP vers HTTPS, quelle que soit la ressource demandée sur le domaine en question.

  • %{HTTP_HOST} fait référence au nom de domaine du site.
  • %{REQUEST_URI} fait référence au chemin vers la ressource demandée.

Certains drapeaux sont définis entre crochets :

  • L garantit qu’aucune autre règle n’est appliquée à cette demande si cette règle est exécutée.
  • R précise le type de redirection à retourner au navigateur.
  • NE permet d’éviter de convertir les caractères spéciaux (comme #, & et ?) en leur équivalent hexadécimal (%23…).

2. Redirection WWW vers le domaine de base

Historiquement, le sous-domaine www (pour « World Wide Web ») était automatiquement attribué aux sites web (www.mondomaine.com), qui n’étaient bien souvent accessibles que par ce dernier. Il n’était pas toujours possible d’y accéder via mondomaine.com.

Aujourd’hui, si certains hébergeurs vont continuer à créer un sous-domaine en www pour chaque site, son usage à tendance à disparaître. Cela se traduit généralement par la mise en place d’une redirection du domaine en www vers le domaine initial, de sorte à ne pas rendre accessible 2 pages avec un contenu identique.

Pourquoi cela ? Car les moteurs de recherche n’apprécient pas le « duplicate content ».

Voici un extrait permettant de rediriger les domaines en www vers le domaine de base :

.htaccess
copié !
# Rediriger www vers le domaine de base
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*) https://%1%{REQUEST_URI} [R=301,L,NE]
  • %{HTTP_HOST} fait référence au nom de domaine du site.
  • %{REQUEST_URI} fait référence au chemin vers la ressource demandée.
  • Dans la condition de réécriture, (.*) capture le nom de domaine qui pourra être récupéré dans %1.
  • Le drapeau NC rend la chaîne insensible à la casse.

3. Réécriture des « query strings »

Nous avons vu au chapitre précédent que la réécriture d’URL permettait d’effectuer des redirections plus complexes au sein d’un fichier 📄 .htaccess.

Mais quand on parle d’URL rewriting, on pense plus généralement à la réécriture sans redirection, mais visant uniquement à en changer leur apparence.

Bien souvent, les URLs d’un site web dynamique contiennent des paramètres GET. Cette portion variable est d’ailleurs aussi appelée « query strings ». Si ces paramètres présentent l’avantage indéniable de pouvoir faire varier le contenu d’un fichier en fonction de leur valeur, côté SEO, il vaut mieux ne pas avoir des attentes trop élevées…

Pourquoi cela ? Et bien car toutes les informations permettant de charger des pages différentes depuis notre fichier 📄 index.php passent par des paramètres GET. Nos URLs sont par conséquent longues, peu explicites et ne laissent pas transparaître de notion de « profondeur ».

Quelle URL préférez-vous ?

  1. https://monsite.com/products.php?category=computer
  2. https://monsite.com/products/computer

Ne dites pas la première, personne ne vous croira. 🙃

L’URL rewriting consistera donc à transformer une URL complexe (contenant généralement des paramètres GET) en une URL plus simple pour l’internaute, mais surtout pour les moteurs de recherche. On parle aussi d’URL « SEO friendly ».

Ainsi, 2 URLs seront différentes, mais pointeront vers une même ressource sur le serveur.

La directive RewriteRule est idéale pour modifier l’apparence de nos query strings.

.htaccess
copié !
# Réécrire les URLs contenant des paramètres de requête GET "category"
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^products/(.*)$ products.php?category=$1 [NC,QSA,L]

%{REQUEST_FILENAME} est une variable Apache qui représente le chemin réel dans le système de fichiers sur le serveur.

  • ! est un symbole qui désigne la négation du filtre qui suit
  • -f précise qu’il s’agit d’un fichier (f pour « file ») et !-f l’inverse.
  • -d précise qu’il s’agit d’un répertoire (d pour « directory ») et !-d l’inverse.

Les deux conditions de réécriture %{REQUEST_FILENAME} !-f et %{REQUEST_FILENAME} !-d excluent ainsi les URL correspondants à un fichier réel ou à un répertoire / sous-répertoire existant sur le site. Cela permet ainsi de ne pas réécrire les chemins vers les ressources (CSS, JS…).

Pour qu’une règle de réécriture ne déclenche pas de redirection (changeant l’URL dans la barre d’adresse), il y a 2 règles à respecter :

  1. L’URL cible doit être écrite via un chemin relatif (pas de http:// ou https:// provoquant une redirection)
  2. La directive RewriteRule ne doit pas contenir de drapeau R=xxx (provoquant également une redirection propre au code HTTP spécifié).

Dans cette RewriteRule, l’URL source cible products.php?category=$1 est écrite via un chemin relatif depuis le répertoire où est situé le fichier 📄 .htaccess et aucune redirection n’est explicitée par le drapeau R=xxx. En conséquence, l’URL ne sera pas modifiée visuellement dans la barre d’adresse du navigateur.

Nos URLs seront donc converties avec des paramètres GET, mais ces derniers seront invisibles !

Pour exemple, l’URL https://monsite.com/products/computer sera transformée en https://monsite.com/products.php?category=computer sans que l’on s’en aperçoive car la barre d’adresse restera inchangée.

  • Le drapeau QSA permet de conserver une chaîne de requête existante (d’autres paramètres d’URL GET) qui aurait par défaut été écrasée par la règle de réécriture. En savoir plus
  • Le drapeau L indique que si la règle de réécriture s’applique, aucune autre règle ne doit être traitée.