Blog sur le web design, le référencement et l'actualité de la société Effi10

Prestashop : Corriger le duplicate content

La plate-forme de commerce en ligne Prestashop™ propose nativement de très bonnes choses en matière de référencement, avec notamment une optimisation SEO de très bonne facture (URL Rewriting intégré, optimisation fine des balises META Description et des titres, structure technique, etc…)

Néanmoins, certains éléments manquaient à la version 1.0 et je vous propose une petite astuce pour corriger l’un d’eux : le risque de duplicate content en cas de changement de noms de produits ou de catégories par l’administrateur, et plus généralement (concernant les catégories) la gestion avancée de la pagination et des options de tri de produits, de manière à ce que ces pages secondaires soient suivies mais non indexées par les moteurs pour ne pas gêner le positionnement des pages principales.

IMPORTANT : les modifications exposées ci-après ne sont que les fondations de ce qu’il est possible de réaliser. Elles ne prennent pas en compte certains modules existants (comme “sendtoafriend” ou encore “contactproduct”) ni aucune modification mettant en œuvre des modifications de variables passées en GET (cas des modules utilisant les technologies AJAX notamment).

Par ailleurs, le code dépend en grande partie du paramétrage serveur (gestion des variables d’environnement et des variables serveur) et il n’est de ce fait pas possible de produire un code générique fonctionnant pour n’importe quelle boutique.

Réécriture des URL et problématique rencontrée

Le phénomène est très aisément décelable, en ce sens qu’une page produit reste accessible par son URL rewritée :

http://ma-boutique.com/10-nom-produit.html

Mais aussi sans contrôle aucun, par toute URL pseudo réécrite de même forme :

http://ma-boutique.com/10-ici-ce-que-vous-voulez.html

ou encore son URL non réécrite :

http://ma-boutique.com/product.php?id_product=10

Dans le cas des pages de catégories, on observe le même phénomène, en y ajoutant les options de pagination de listes de produits, ainsi que les options de tri :

http://ma-boutique.com/10-nom-categorie

Même page avec accès non réécrit :

http://ma-boutique.com/category.php?id_category=10

ou bien réécrite mais avec pagination (ici 3ème page) :

http://ma-boutique.com/10-nom-categorie?p=3

ou encore réécrite mais avec tri des produits sur le prix du moins cher au plus cher (nb : options cumulables avec la pagination !) :

http://ma-boutique.com/10-nom-categorie?orderway=ASC&orderby=price

Le principal soucis est que ces n formes d’URL peuvent potentiellement être détectées et indexées par les moteurs de recherche et notamment Google, et de ce fait se faire concurrence puisque leur contenu sera résolument identique (à quelques détails près si le phénomène est uniquement dû au changement du nom du produit par l’administrateur de la boutique !) ou en tout cas très proche, dans le cas des tris et paginations de catégories.

Qui dit contenu dupliqué, dit pénalisation dans le positionnement voire désindexation, et donc indirectement, chute du chiffre d’affaire !

La solution

Le principe repose sur deux choses simples :

  • détecter si l’on se trouve sur une page produit ou catégorie
  • retrouver l’URL théoriquement correcte et le cas échéant, rediriger la page vers cette dernière si celle utilisée est différente !

Une limite technique de PHP fait que nous ne pouvons rediriger une page (en modifiant l’entête HTTP à la volée) que si aucune donnée n’a été balancée dans le flux HTML ; en d’autres termes, la détection doit impérativement se faire avant d’afficher quoi que ce soit ! La structure de Prestashop™ nous oblige donc à agir dans le premier fichier affichant des données dans le flux : header.php

Voici le code à insérer dans le fichier header.php (que vous trouverez à la racine de votre boutique Prestashop™) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php
 
require_once(dirname(__FILE__).'/init.php');
 
/* CODE A INSERER A PARTIR D'ICI */
 
/***** Test et correction des URL non valides *****/
if (isset($_GET['id_product']) AND Validate::isUnsignedId($_GET['id_product']))
{
	// Nous sommes sur une page produit
	$cookie = new Cookie('ps');
	Tools::setCookieLanguage();
	$product = new Product(intval($_GET['id_product']), true, intval($cookie->id_lang));
	if (Validate::isLoadedObject($product) AND $product->active)
	{
		$goodurl=$_GET['id_product']."-".$product->link_rewrite.".html";	// URL theorique
		$currenturl=basename($_ENV['REQUEST_URI']);				// URL courante
 
		$redir=strcmp($goodurl, $currenturl);
		if($redir!=0)
		{
			// Pas la bonne URL donc redirection HTTP 301 vers l'URL correcte
			header('HTTP/1.1 301 Moved Permanently', false, 301);
			header("Location: http://" . $_SERVER["SERVER_NAME"] ."/". $goodurl);
			exit(); 
		}  
	};
}
 
if (isset($_GET['id_category']) AND Validate::isUnsignedId($_GET['id_category']))
{
	// Nous sommes sur une page Categorie
	$cookie = new Cookie('ps');
	Tools::setCookieLanguage();
	$category = new Category(intval(Tools::getValue('id_category')), intval($cookie->id_lang));
	if (Validate::isLoadedObject($category) AND $category->active)
	{
		$paramurl="";
 
		// Detection d'un eventuel numero de page
		if (isset($_GET['p'])) {
			$varpagenumber=$_GET['p'];
			if($varpagenumber>1) {
				$paramurl="?p=$varpagenumber";
				$smarty->assign('nobots', 1);	// variable Smarty de Prestashop qui appose la valeur "noindex,follow" dans la balise META Robots
			};
		};
 
		// Detection des variables eventuelles de tri de produits
		if (isset($_GET['orderby'])) {
			$varorderby=$_GET['orderby'];
			$paramurl.=(empty($paramurl)?"?":"&")."orderby=$varorderby";
			$smarty->assign('nobots', 1);		// variable Smarty de Prestashop qui appose la valeur "noindex,follow" dans la balise META Robots
		};
		if (isset($_GET['orderway'])) {
			$varorderway=$_GET['orderway'];
			$paramurl.=(empty($paramurl)?"?":"&")."orderway=$varorderway";
			$smarty->assign('nobots', 1);		// variable Smarty de Prestashop qui appose la valeur "noindex,follow" dans la balise META Robots
		};
 
		$goodurl=$_GET['id_category']."-".$category->link_rewrite.$paramurl;	// URL theorique
		$currenturl=basename($_ENV['REQUEST_URI']);				// URL courante
		$redir=strcmp($goodurl, $currenturl);
		if($redir!=0)
		{
			// Pas la bonne URL donc redirection HTTP 301 vers l'URL correcte
			header('HTTP/1.1 301 Moved Permanently', false, 301);
			header("Location: http://" . $_SERVER["SERVER_NAME"] ."/". $goodurl);
			exit(); 
		}
	};
}
 
// FIN DU CODE A INSERER

Et Prestashop 1.1 ?

La dernière version du script (au moment où j’écris cet article) a changé la réécriture d’URL pour les pages produit, qui intègrent désormais dans leur URL la catégorie et le cas échéant les sous-catégories de classification de l’article.

Cela a une incidence sur les produits potentiellement classables dans des catégories différentes (ce qui peut arriver) car les pages produit correspondantes seront inévitablement dupliquées, avec des URL différentes, ce qui engendrera du duplicate content à terme dans les moteurs de recherche.

La solution consiste donc à supprimer cette nouvelle réécriture d’URL ; pour cela, il suffit de se rendre dans le répertoire Classes à la racine de votre boutique, et d’éditer le fichier Link.php et plus particulièrement les lignes suivantes :

1
2
3
4
5
6
7
8
9
10
11
12
    public function getProductLink($id_product, $alias = NULL, $category = NULL, $ean13 = NULL)
    {
         if (!isset($this->allow)) $this->allow = 0;
        if (is_object($id_product))
            return ($this->allow == 1)?(__PS_BASE_URI__.intval($id_product->id).'-'.$id_product->link_rewrite.($id_product->ean13 ? '-'.$id_product->ean13 : '').'.html') : 
            (_PS_USE_SSL_.__PS_BASE_URI__.'product.php?id_product='.intval($id_product->id));
        elseif ($alias)
            return ($this->allow == 1)?(__PS_BASE_URI__.intval($id_product).'-'.$alias.($ean13 ? '-'.$ean13 : '').'.html') : 
            (_PS_USE_SSL_.__PS_BASE_URI__.'product.php?id_product='.intval($id_product));
        else
            return _PS_USE_SSL_.__PS_BASE_URI__.'product.php?id_product='.intval($id_product);
    }

Il s’agit en fait de modifier la fonction getProductLink, qui renvoie le lien “bien formé” de notre produit… en recopiant le code de cette fonction de la version 1.0 de la boutique ;-) (ndlr : astuce donnée par Cameleon sur le forum Prestashop)

On se retrouve ainsi dans la configuration d’une version 1.0 et l’on peut donc appliquer de la même manière, les modifications ci-dessus pour corriger en temps réel les pages dupliquées !

Mise en œuvre sur votre boutique Prestashop™

Vous désirez mettre en oeuvre ces modifications sur votre boutique ? Nous proposons une prestation d’optimisation sur mesure, permettant d’éradiquer le duplicate content de votre site marchand, mais aussi d’optimiser le linkjuice interne à votre site, et bien d’autres choses. Pour plus d’information, découvrez notre pack Référencement Prestashop.




14 commentaires pour le moment... ajouter un commentaire ?

  1. Le 23 février 2009 à 12:26, shams a écrit :

    Bonjour,
    Et merci pour ces scripts bien utiles… Mais, comment faire lors d’une installation de Presta dans un sous-répertoire ?…

    Merci de votre attention.

  2. Le 23 février 2009 à 14:08, Cédric G. a écrit :

    Bonjour

    J’avoue ne pas m’être penché sur le problème… En théorie il devrait être suffisant de rajouter le répertoire juste dans les URL reconstruites dans le code.

    Mais bon je n’ai aucune expérience de boutique installée dans un sous-répertoire.

  3. Le 24 février 2009 à 10:41, shams a écrit :

    Merci Cédric, voulez-vous dire ligne 24 et 68 du script (je n’arrive pas à entrer le code dans le commentaire) après SERVER_NAME en changeant le slash par /repertoire/ par exemple ?…

  4. Le 24 février 2009 à 11:12, Cédric G. a écrit :

    Je pense que cela devrait effectivement suffire.

  5. Le 24 février 2009 à 11:24, shams a écrit :

    Heu… et bien non justement, cela ne fonctionne pas…

  6. Le 24 février 2009 à 11:53, Cédric G. a écrit :

    Pour le débogage, incluez le code suivant :

    echo $currenturl.”< br / >“.$goodurl;

    juste avant les strcmp qui servent à comparer les valeurs (lignes 18 et 64)

    Ça vous aidera grandement à comprendre ce qui ne va pas et affichant la valeur lue et la valeur théorique reconstruite.

  7. Le 24 février 2009 à 12:39, shams a écrit :

    J’ai intégré votre code à la ligne après le commentaire // URL courante
    C’est bien ça ?… Avec ou sans, cela donne une erreur 404…

  8. Le 24 février 2009 à 13:39, Cédric G. a écrit :

    Commentez les lignes de code de header ou bien mettez un “exit;” juste après les echo ajoutés précédemment (cela arrêtera le script et vous verrez s’afficher la valeur des variables)

  9. Le 24 février 2009 à 15:02, shams a écrit :

    Désolé, page blanche et, aucune valeur ne s’affiche !…

  10. Le 16 avril 2009 à 10:08, Grimaud a écrit :

    Salut Cédric,
    Concernant le problème de la version 1.1 (ou 1.2.0.1) avec la nouvelle règle d’écriture (qui prends en compte la catégorie du produit) et qui génère du duplicate content que penses tu de cette méthode :
    1. Admettons qu’un produit appartienne à 2 catégories, categorie 1 et catégorie 2 (catégorie 2 étant définie par défaut dans le back office).
    2. Dans le fichier header.php, il suffirait de tester la catégorie renvoyée par getProductLink (), si celle-ci n’est pas la catégorie par défaut alors on redirige vers la page réécrite avec la catégorie par défaut.
    3. Je n’ai pas le code source de Presta sous les yeux, mais il me semble qu’il y a une méthode dans la classe Product.php qui permet de récupérer la valeur de la catégorie par défaut du produit et qui pourrait donc être bien utile ici.
    Dans l’attente de te lire.

  11. Le 16 avril 2009 à 22:23, Cédric G. a écrit :

    Bonjour Grimaud

    C’est une méthode parfaitement valable tant que l’on s’assure qu’au final, tout produit a une unique URL ;-)

    Je suis par contre plus réservé quant à l’intérêt d’une telle méthode d’un point de vue purement référencement… et au niveau pratique : si l’internaute est dans la catégorie 2 et qu’il clique sur le produit, il va donc arriver sur ce dernier “virtuellement” via la catégorie 1 (la catégorie principale).

    Le risque est de dérouter cet utilisateur vers une catégorie qui ne contient potentiellement pas exactement ce qu’il recherche…

    Mais d’un point de vue purement technique, c’est correct !

  12. Le 10 septembre 2009 à 11:03, Jacques a écrit :

    Bonjour
    est ce qu’il y a une évolution ou est compatible pour la Version 1.2.0.8 ?
    Cordialement
    Jacques

  13. Le 10 septembre 2009 à 12:03, Cédric G. a écrit :

    Bonjour

    Ce type de modification fonctionne toujours avec la version 1.2 même s’il y a effectivement eu quelques améliorations.

    Il faut néanmoins veiller à ce que tout colle bien (nb : je n’ai pas encore fait d’optimisation sur des boutiques en v1.2 à ce jour)

  14. Le 10 septembre 2009 à 16:32, Jacques a écrit :

    Bonjour
    Merci de ta réponse, mais apparemment il y a un problème , Google bloque sur le scan de mon sitemap , je me retrouve avec une croix rouge ..je regarderai cela de plus près après le WE
    bonne journée
    Jacques

Laisser un commentaire

ndlr : ce blog affiche les liens de sites des contributeurs en "dofollow" ; néanmoins, les pseudos "SEO" ne sont pas les bienvenus et seront le cas échéant modérés.