Comment changer de réécriture de liens ?

Guillaume Rossolini

Ce tutoriel explique comment il est possible de changer d'URL Rewriting (ou bien simplement le mettre en place) sans perdre le référencement auprès des moteurs de recherche.

Attention, il n'est pas question ici d'expliquer comment changer de méthode de réécriture de liens. Ce tutoriel se contentera de vous sensibiliser aux problèmes posés par un tel changement et vous apportera les solutions qui conviennent.

Commentez ♪

- Titre : Comment changer de réécriture de liens ?

- Auteur : Guillaume Rossolini

- Parution : 19 juin 2006

- Licence : Copyright ® 2006 Guillaume Rossolini. 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

- I. Introduction

- I-A. Remerciements

- I-B. Préambule

- II. Travail préliminaire

- II-A. Problématique

- Générale

- Liens externes

- Liens internes

- II-B. Tour du propriétaire : que devons-nous changer ?

- III. Implémentation

- III-A. Liens externes

- III-B. Liens internes

- IV. Conclusion

- IV-A. Épilogue

- IV-B. Liens

I - Introduction

I-A - Remerciements

L'équipe Web pour m'avoir relu et conseillé (en particulier Mathieu Lemoine), ainsi que Florian Lapôtre pour m'avoir sensibilisé à tout cela.

I-B - Préambule

Dans un article précédent, j'ai expliqué comment il est possible de mettre en place une technique de réécriture de liens pour un site dynamique, de manière à améliorer le référencement des pages auprès des moteurs de recherche.
Cependant, j'ai également précisé dans la conclusion de cet article que la méthode présentée est incomplète.
En effet, le serveur (le fichier .htaccess) ne tient pas compte des informations que nous ajoutons aux liens, ce qui signifie que nous pouvons tomber dans une situation de duplicate content. Il suffit pour cela qu'un utilisateur poste un lien http://exemple.com/sujet-12,articles.htm et qu'ensuite le titre du sujet lié (« Articles ») soit modifié (« Liste des articles de SEO ») : les URL dynamiques seront automatiquement mises à jour (http://exemple.com/sujet-12,liste-des-articles-de-seo.htm) dans l'ensemble du site, mais pas ce lien. C'est une situation de duplicate content.

De plus, il arrive que l'on change de technique d'URL Rewriting (bien que cela ne soit pas souhaitable). Dans le cas du Forum Cinéma dont je parle dans l'autre tutoriel, cela a eu lieu plusieurs fois (et pas toujours sans mal)…

L'objectif de ce tutoriel est d'étudier les aspects nécessaires à une redirection sans heurt (particulièrement pour les moteurs de recherche) vers les nouvelles pages. Je vais prendre appui sur mon précédent tutoriel et y greffer la nouvelle solution.

Il est fortement conseillé de s'être familiarisé avec les expressions régulières avant d'entamer la lecture de ce tutoriel.

II - Travail préliminaire

II-A - Problématique

Générale

Étudions une situation pour illustrer tout cela. Je vais utiliser des dates fictives pour simuler l'écoulement du temps.

Le 23 octobre 2000, nous installons un forum phpBB non modifié. Nos pages ont des adresses du style :

- viewtopic.php?t=[0-9]+

- viewforum.php?f=[0-9]+

- etc.

Le 13 avril 2004, nous désirons améliorer le référencement de ce forum auprès des moteurs de recherche. Nous installons donc une méthode de réécriture de liens qui transforme les liens de la manière suivante :

- ftopic[0-9]+.html

- forum[0-9]+.html

- etc.

Le 23 octobre 2005, nous nous apercevons que nous pourrions ajouter des mots clefs à ces adresses. Ainsi, nous modifions la technique de réécriture pour obtenir :

- sujet-[0-9]+,[0-9a-z-]+.html

- forum-[0-9]+,[0-9a-z-]+.html

- etc.

Ici, les [0-9]+ représentent les paramètres nécessaires au bon fonctionnement de phpBB.
Les [0-9a-z-]+ représentent les listes de mots clefs, qui sont des informations utiles uniquement pour le référencement (non pour le bon fonctionnement de phpBB). Ce sont en partie elles qui nous posent problème, malgré la raison évidente de leur présence.
La dernière situation illustre la méthode proposée dans le tutoriel précédent.

Le problème est que, tout au cours de la vie du forum, les utilisateurs ont posté des liens (dans le forum lui-même ou à l'extérieur). Cela a pu se produire entre chaque étape, nous avons donc certainement des liens écrits de trois manières différentes, mais qui fonctionnent tous de manière équivalente. Ces liens ne sont pas gérés automatiquement par phpBB car ils sont traités comme de l'information plutôt que comme des liens. À ce titre, les adresses contenues dans les messages n'évoluent pas lorsque nous modifions la technique de réécriture de liens.
En conséquence, de tels liens redirigent vers le même contenu que des liens équivalents qui ont été réécrits. Cela nous met en situation de duplicate content et, tel que nous l'avons établi dans le tutoriel précédent, c'est très mauvais pour notre référencement. Il faut donc y remédier.

Différentes URI pour le même résultat

Il y a deux cas : les liens provenant de l'extérieur de notre site et ceux internes à notre site.

Liens externes

Ce sont des backlinks : il est évident que nous n'allons pas pouvoir tous les modifier. Peut-être, sommes-nous l'auteur de quelques-uns, mais rien ne garantit que nous n'en oubliions aucun. De plus, il est fort probable que d'autres personnes aient copié ailleurs des liens de notre site, voire même que les moteurs de recherche se souviennent de ces anciennes adresses.
Les raisons sont nombreuses, le jugement est sans appel : il faut avertir les visiteurs utilisant des adresses obsolètes qu'ils doivent désormais utiliser les nouvelles.

Les liens sont restaurés à leur état initial avant d'être réécrits. Aucune page n'est transmise au navigateur tant qu'il n'a pas demandé l'adresse correcte.

Liens internes

Nous avons un certain contrôle sur le contenu de notre site. En réalité, nous pouvons masquer le fait qu'il y ait eu diverses méthodes de réécriture de liens.

En somme, il s'agit d'insérer une étape de réécriture de liens avant la réécriture finale des liens. La différence est que, cette fois, les liens réécrits ne parviendront pas jusqu'au navigateur. Au contraire, ils seront immédiatement à nouveau réécrits.
Pour cela, nous aurons simplement besoin d'une liste d'adresses à réécrire. Dans la réécriture complète de liens (l'autre tutoriel), nous avons besoin de déterminer des mots clefs et tout un tas de choses. Ici, l'objectif est justement de retrouver les adresses d'origine (donc de supprimer les informations ajoutées par une méthode antérieure d'URL Rewriting). Il ne sera pas nécessaire d'envoyer des requêtes à une base de données.

Les liens sont restaurés à leur état initial avant d'être réécrits. Aucune page n'est transmise au navigateur avant la fin du traitement.

II-B - Tour du propriétaire : que devons-nous changer ?

Nous allons commencer par identifier les URL que nous devrons restaurer (les adresses de destination, en quelque sorte). Théoriquement, cela devrait beaucoup ressembler à ce que nous avons fait dans l'autre tutoriel (quoique dans le sens l'inverse). Je vais directement proposer une syntaxe prévue pour être utilisée dans la fonction preg_replace() (remplacement de chaînes au moyen d'une expression régulière). Cela nous simplifiera la tâche puisque cette syntaxe est déjà utilisée dans le fichier .htaccess.
Je vais continuer à prendre pour exemple un forum phpBB.

Chaque RewriteRule du fichier .htaccess nous sert donc à construire cette liste :

- viewtopic.php?t=$1

- viewforum.php?f=$1

- etc.

Maintenant que nous savons où nous devons aller, il est temps de recenser les URL à restaurer. Il s'agit de toutes les URL qui ne sont pas au format standard de phpBB : nom du script + paramètres.

Voici quelques exemples d'adresses utilisées par certaines méthodes de réécriture de liens pour phpBB :

- ftopic[0-9]+ pour les sujets ;

- forum[0-9]+ pour les forums ;

- etc.

III - Implémentation

Je vous propose de voir l'implémentation en deux étapes : une première approche nous permettra de voir comment fonctionne chaque mécanisme puis nous aborderons l'approche complète.
La raison de cette séparation est que nous avons le contrôle sur les liens figurant à l'intérieur de notre propre site, ce qui signifie que nous pouvons les modifier sans que l'utilisateur final en ait connaissance. Cela nous permet d'éliminer peu à peu les liens périmés.
En revanche, les liens situés à l'extérieur de notre site sont hors de notre portée. Il nous faut donc indiquer aux visiteurs qui utilisent ce type de lien qu'il est recommandé de mettre leurs favoris à jour.

III-A - Liens externes

Cela se fait au moyen d'un code HTTP/1.1 : lorsqu'un visiteur utilise une adresse que nous savons mauvaise, il nous faut employer le code 301 (« déplacé de manière permanente »). Nous indiquons ensuite au visiteur l'emplacement de l'adresse correcte.

Redirection à partir d'une adresse incorrecte

<?php

header('HTTP/1.1 301 Moved Permanently');
header('Location: nouvelle-adresse.ext');

exit; // Il ne faut rien envoyer d'autre

?>

Dans la pratique, il nous faut d'abord retrouver (restaurer) l'adresse d'origine puis utiliser notre réécriture de liens afin de rediriger vers la bonne adresse.
La première phase (la restauration) est effectuée au moyen du fichier .htaccess : nous substituons la mauvaise adresse par un script de redirection auquel nous indiquons l'adresse standard (nom physique du script + paramètres). De cette manière, il suffit au script de redirection de réécrire cette adresse selon les règles de réécriture que nous avons définies dans le tutoriel précédent.

Fichier : .htaccess

DirectoryIndex index.php
Options +FollowSymlinks
RewriteEngine on

#Restauration des URL de base
RewriteRule ^ftopic([0-9]+).* /phpbb-test/redirect.php?url=viewtopic.php?t=$1 [L]
RewriteRule ^forum([0-9]+).* /phpbb-test/redirect.php?url=viewforum.php?f=$1 [L]

#Réécriture normale
RewriteRule ^sujet-([0-9]+).* /phpbb-test/viewtopic.php?t=$1 [L]
RewriteRule ^forum-([0-9]+).* /phpbb-test/viewforum.php?f=$1 [L]

Nous retrouvons dans ce fichier .htaccess les trois types d'URL que nous avons vus tout à l'heure dans la Problématique : les URL de base (viewtopic.php?t=$1), intermédiaires (ftopic[0-9]+.*) et finales (sujet-[0-9]+.*).

Fichier : redirect.php

<?php

define('IN_PHPBB', true);

$phpbb_root_path = './';

include($phpbb_root_path . 'extension.inc');
include($phpbb_root_path . 'common.'.$phpEx);

if(!empty($userdata['user_lang'])){
$current_language = $userdata['user_lang'];
}
else{
$current_language = $board_config['default_lang'];
}

include($phpbb_root_path.'/language/lang_'.$current_language.'/lang_urlrewrite.'.$phpEx);
if(is_array($lang['urlrewrite'])){
require_once($phpbb_root_path.'includes/functions_urlrewrite.'.$phpEx);

$new_url = rewrite_url(urldecode($_GET['url']), $lang['urlrewrite']);

header('HTTP/1.1 301 Moved Permanently');
header('Location: '.$new_url['url']);
exit;
}

?>

L'appel à urldecode() est rendu nécessaire par les scripts utilisant plusieurs paramètres (ils sont légion) et d'une contrainte imposée par notre .htaccess, dans lequel nous avons simulé un appel à urlencode().

Pourquoi urldecode() est nécessaire :

- /phpbb-test/redirection.php?url=viewtopic.php?t=1&start=25 ==> il y a confusion pour le paramètre « start » qui peut ici être envoyé au script « redirection.php » (ce n'est pas ce que nous voulons) ;

- /phpbb-test/redirection.php?url=viewtopic.php?t=1&amp;start=25 ==> « start » n'est pas encore compris comme un paramètre, mais comme faisant partie intégrante du paramètre « url » (c'est ce que nous voulons).

Si notre site a subi plusieurs réécritures successives de ses liens, il suffit de compléter la partie « Restauration des URL de base » de notre fichier .htaccess !

Fichier : .htaccess

DirectoryIndex index.php
Options +FollowSymlinks
RewriteEngine on

#Restauration des URL de base
RewriteRule ^ftopic([0-9]+).* /phpbb-test/redirect.php?url=viewtopic.php?t=$1 [L]
RewriteRule ^forum([0-9]+).* /phpbb-test/redirect.php?url=viewforum.php?f=$1 [L]
RewriteRule ^viewtopic.php?t=([0-9]+) /phpbb-test/redirect.php?url=viewtopic.php?t=$1 [L]
RewriteRule ^viewforum.php?f=([0-9]+) /phpbb-test/redirect.php?url=viewforum.php?f=$1 [L]

#Réécriture normale
RewriteRule ^sujet-([0-9]+).* /phpbb-test/viewtopic.php?t=$1 [L]
RewriteRule ^forum-([0-9]+).* /phpbb-test/viewforum.php?f=$1 [L]

III-B - Liens internes

Si nous observons l'un des fichiers de langue de l'autre tutoriel, nous pouvons remarquer la présence de clefs pour le tableau $lang['urlrewrite']['regexes']. Nous allons utiliser les mêmes clefs dans notre tableau actuel afin de pouvoir faire correspondre les URL sans problème.

Pour restaurer les URL, il nous faut mettre les adresses « brutes » dans un tableau à part que nous appellerons $raw_URL. Nous utiliserons un autre tableau, $rewritten_URL, pour contenir les différentes versions de chaque adresse réécrite : l'index « old » sert aux adresses construites par d'anciennes versions de la réécriture de liens de notre site. Chacun des autres index correspond à une langue installée dans notre forum phpBB : les utilisateurs ont pu poster des adresses dans toutes ces différentes langues, il faut donc uniformiser la présentation.
Dans tous ces tableaux, nous utiliserons les clefs que nous avons remarquées ci-dessus.

$raw_URL & $rewritten_URL

<?php

$raw_URL = array(
'viewtopic_topicid_title'  => 'viewtopic.php?t=$1',
'viewforum_id_title'       => 'viewforum.php?f=$1',
// ...
);

$rewritten_URL['old'] = array(
'viewtopic_topicid_title'   => array('ftopic([0-9]+)'),
'viewforum_id_name'         => array('forum([0-9]+)'),
// ...
);

// ...

if($directory = dir($phpbb_root_path . 'language')){
while($entry = $directory->read()){
if(is_file($phpbb_root_path . 'language/' . $entry . '/lang_urlrewrite.'.$phpEx)){
include($phpbb_root_path . 'language/' . $entry . '/lang_urlrewrite.'.$phpEx);
$rewritten_URL[basename($entry)] = $lang['urlrewrite']['regexes'];
}
}
}

?>

Il nous reste maintenant à restaurer les URL par défaut de phpBB.
J'ai choisi de commencer par restaurer les URL par défaut pour ensuite laisser le soin à notre méthode de réécriture de se charger du reste des opérations de la même manière qu'elle en a l'habitude.

Un point de sémantique : dans ce cas précis, « restaurer » signifie retrouver une URL par défaut de phpBB à partir d'une URL réécrite, tandis que « réécrire » signifie modifier l'URL par défaut en une URL réécrite. Les deux termes peuvent être utilisés dans les deux situations, c'est pourquoi je préfère être clair.

Afin de récupérer les URL de la page, j'ai modifié le code utilisé dans includes/page_tail.php :

Récupération des URL à restaurer puis à réécrire (fichier : includes/page_tail.php)

require_once('functions_urlrewrite.'.$phpEx);
//
// Find all the links in the page
// There are 4 parenthesized parts in these regular expressions:
//   #1: there might be an option like class="..."
//   #2: full path to the target Web page
//   #3: #1 bis
//   #4: text of the link (optional)
//

$regexes = array(
'a'    => '#<a(.+)href="([^"]+)"([^>]*)>(.*)</a>#Usi',
'form' => '#<form(.+)action="([^"]+)"([^>]*)>#Usi',
'link rel'  => '#<link rel(.+)href="([^"]+)"([^>]*)>#Usi'
);

foreach($regexes as $link_type => $pattern){
$matches = array();
if($link_type == 'a'){ // Restore only anchor-type links
if(preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)){
$contents = restore_URL($contents, $matches);
}
}

$matches = array();
if(preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)){
$contents = rewrite_URL($contents, $matches, $link_type);
}
}

Étudions maintenant la fonction restore_URL().
Le principe est similaire à celui de la fonction rewrite_URL() : commencer par répertorier toutes les URL à réécrire, puis tout traiter d'un coup à l'aide de la fonction str_replace().

Fonction restore_URL() dans le fichier : includes/functions_urlrewrite.php

/*
* Restore URL to their original (not rewrited) state
* it builds a string suitable for URL
* @param    string     $string     The string to rewrite
* @global   array      $lang       The language variables
* @return   string                 The rewritten string
*/
function restore_URL($contents, $matches){
global $raw_URL, $rewritten_URL;

$patterns       = array();    // The link patterns will be added to this array
$replacements   = array();    // The link replacements will be added to this array

foreach($matches as $match){
$current_url = trim($match[2]);

if(in_array($current_url, $patterns)){
continue; // Skip this match
}

foreach($rewritten_URL as $pool){
//
// Old and new rewriting (handled as a pool of URL)
//
foreach($pool as $key => $regexes){
//
// Each rewrited URL in this URL pool (usually: one)
// These URL should be available in a generic form
// using a regular expression syntax (without delimiters)
//
foreach($regexes as $key => $regex){
$regex = '/'.$regex.'/';
if(preg_match($regex, $current_url)){
$changed_url = preg_replace($regex, $raw_URL[$key], $current_url);
$patterns[] = 'href="'.$current_url.'"';
$replacements[] = 'href="'.$changed_url.'"';
break 2;
}
}
}
}
}

return str_replace($patterns, $replacements, $contents);
}

IV - Conclusion

IV-A - Épilogue

Je vous ai présenté ici une méthode permettant de mettre en place successivement plusieurs techniques de réécriture de liens sans que le référencement de votre site en souffre. À vous de savoir adapter les exemples de ce tutoriel à votre cas !
Je reste à votre disposition pour toute question. Qui sait, peut-être un jour mettrai-je à disposition un script complet permettant de faire le pont entre toutes les techniques existantes !

IV-B - Liens

- Request For Comments 2616 : « HTTP/1.1 Status Code Definitions » (particulièrement le chapitre : 10.3 Redirection 3xx)

- Tutoriel : les expressions régulières par Guillaume Rossolini

- Tutoriel : le Protocole HTTP par Mathieu Lemoine