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 déclassement dans le positionnement, 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™) :
<?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 :
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™
On peut bien évidemment aller beaucoup plus loin au niveau technique, mais n’oubliez pas de réaliser une sauvegarde de votre boutique ET de votre base de données (même si ce type de modification n’est censé avoir aucun incidence sur les données) avant toute mise en place en production !
50 commentaires sur Prestashop 1.0 à 1.3 : Corriger le duplicate content
shams 23 février 2009
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.
Cédric G. 23 février 2009
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.
shams 24 février 2009
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 ?…
Cédric G. 24 février 2009
Je pense que cela devrait effectivement suffire.
shams 24 février 2009
Heu… et bien non justement, cela ne fonctionne pas…
Cédric G. 24 février 2009
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.
shams 24 février 2009
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…
Cédric G. 24 février 2009
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)
shams 24 février 2009
Désolé, page blanche et, aucune valeur ne s’affiche !…
Grimaud 16 avril 2009
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.
Cédric G. 16 avril 2009
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 !
Jacques 10 septembre 2009
Bonjour
est ce qu’il y a une évolution ou est compatible pour la Version 1.2.0.8 ?
Cordialement
Jacques
Cédric G. 10 septembre 2009
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)
Jacques 10 septembre 2009
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
besson 5 août 2010
Bonjour,
Merci pour cette transformation du header qui évite pas mal de duplicitate mais pas tous… en effet je me suis aperçu que mes pages produits sont toujours accessibles par la catégorie; De plus, j’ai noté un petit problème à la suite de ce changement qui peut etre est spécifique à mon site: mon site est bilingue avec un sous domaine pour l’anglais et lorsque je change la devise du site, sur une autre page que la page d’accueil, la devise choisie n’est pas conservée!
Auriez vous une solution à ce léger problème?
Merci d’avance.
Cédric 12 septembre 2010
Bonjour
Concernant les devises, sincèrement aucune idée !
Une piste (puisque ça doit être un problème de cookie) : avez-vous « inscrit » le sous-domaine au niveau du backoffice dans les sous-domaines « autorisés » ?
Ludwig Wourms 25 octobre 2010
Bonjour,
J’ai essayé votre code pour ma boutique. Mais au lieu de renvoyer vers la page actuellement rewritté, les anciens liens se dirigent vers l’accueil…
J’ai cherché pendant des heures des erreurs dans le script, dans mes fichiers générant le php… Je ne trouve pas. Avez vous déjà détecté des problèmes similaires ?
Ce pb de duplicate content est une vrai plaie pour le référencement…
SANSelle 16 novembre 2010
Bonjour,
j’essaie de mettre en place la redirection sur mon site.
La redirection marche en français. Toutefois, pour l’anglais ça coince. “lang-en” se rajoute à l’url, or ce dossier n’existe pas.
Faut-il que je créer un nouveau répertoire “lang-en” ou y-a-t-il quelque chose à changer dans le code php?
Merci beaucoup
Cédric Girard 17 novembre 2010
Bonjour
Je ne saurais que vous conseiller soit de virer la langue anglaise, soit de la gérer différemment car la gestion par « sous-répertoire » virtuel dans Prestashop est une hérésie d’un point de vue SEO.
Reportez-vous à l’article de Vemeo (recherchez « prestashop multilingue ») dans un premier temps. Ensuite, il est facile d’adapter ma modification…
Dès que j’aurai trouvé le temps de finaliser le nouveau site effi10, je publierai un article complet sur les deux choses que sont la gestion du duplicate content dans Prestashop, et la gestion efficace d’une boutique bilingue (avec gestion en sous-domaines !)
SANSelle 17 novembre 2010
D’accord, je pense que je vais utiliser votre méthode car je suis plus à l’aise avec PHP et je suis encore débutant en référencement.
Juste avec votre méthode, je dois bien activer l’url rewriting sur prestashop?
Et dois-je modifier mon .htaccess ou cette méthode évite cela?
SANSelle 19 novembre 2010
Heu problèmes avec cette méthode.
PREMIER PROBLEME :
http://www.site.fr/perruque/891-perruque.html
=
http://www.site.fr/891-perruque.html
DEUXIEME PROBLEME :
http://www.site.fr/100-underwear
–> 301 –>
http://www.site.fr/100-lingerie
Du coup, il n’y a plus de pages en anglais…
Quelqu’un a-t-il une solution?
Merci
Cédric G. 14 décembre 2010
Bonjour
Sur les sites bilingues (ou plus) il faut IMPÉRATIVEMENT gérer les langues sur des sous-domaines (c’est de loin la solution la plus simple à mettre en oeuvre)
Couplée à la gestion du DC en temps réel, les résultats sont excellents. CF l’un des sites sur lesquels j’ai appliqué cette méthode (anglais+français) => http://www.mastermateriel.com
Aucun DC, un sous-domaine = une langue (ce qui permet la géolocalisation), un seul cookie pour les paiements (ce qui permet de n’utiliser qu’un seul module de paiement !…)
Il faut donc détecter la langue avant la redirection (soit avec le cookie de langue si vous restez sur un seul sous-domaine, soit – comme je l’ai fait – forcer la langue selon le sous-domaine utilisé, les deux sous-domaines pointant au même endroit bien entendu)
david 26 janvier 2011
Bonjour à tous, pourriez vous m’indiquez comment puis je insérer la balise google sur prestashop1.3.5, pour pouvoir bénéficier des supers outils webmasters google ?
merci
triboulé 16 février 2011
y a t il toujous un meme probleme ave presta 1.3.5?
Cédric G. 16 février 2011
Bonjour
Oui, dans une moindre mesure sur certains points, mais le DC n’est pas corrigé.
La version 1.4 apporte quelques améliorations (notamment au niveau de la gestion multilingue) mais ce n’est pas encore la panacée.
triboulé 17 février 2011
quels sont les modifs seo por la 1.3.5 ou la 1.3.6
pour le duplicate, plug seo fix?
on remarque des problemes d indexation?
merci!
mickagolberg 5 mars 2011
bonjour
devons nous inserer un code pour duplicate content
ou pas la peine dans version 1.3.7?
mickagolberg 12 mars 2011
quel module pour le duplicate seo crol fix?
jal 25 mars 2011
Est ce que cette méthode permet de désindexer duplicate content que Google a déjà trouvé?
Loïc Besson 26 mai 2011
Bonjour,
Je reviens sur le problème que j’avais évoqué concernant le changement de devise.
Sur les pages produits et catégorie, la modification de Cédric rendait le changement de devise non persistant (la dvise se change bien sur la page, mais lorsqu’on passe à une autre page la devise revenait à sa valeur initiale!).
Pour remèdier à cela il suffit d’ajouter sous Tools::setCookieLanguage();
Tools::setCurrency();
Voila, si ça peut aider quelqu’un…
mickagolberg 6 août 2011
passer sur wordpress?
Cédric G. 29 septembre 2011
MMMmm… moui : un WordPress mal rempli aura lui aussi du DC 😉
Max 6 mars 2012
Bonjour,
Et pour la version 1.4 ? Pour moi le problème y est toujours présent pourtant tout le monde à l’air de dire que le problème a été réglé. Dans les faits, je constate que une page reste accessible via les différentes URL listé en début d’article.
Cédric GIRARD 6 mars 2012
Bonjour Max
Je n’ai (toujours) pas avancé sur la v1.4.x, faute de temps, mais des tests que j’ai réalisé sur les versions en démo, à priori une partie du DC est bien géré…
Je ne suis pas allé au fond des choses, il est fort possible que tout ne soit pas correctement géré.
Je n’ai toujours pas eu le temps de rédiger l’article COMPLET sur la gestion du DC, ne serait-ce que pour les plus vieilles versions, mais ça va venir. Patience !
Noraline 7 avril 2012
Du coup, faut-il apporter la correction que vous donner ou la version 1.4 résous le problème automatiquement?
Cédric GIRARD 8 avril 2012
La version 1.4 résout en grand partie le problème 😉
Je dois faire (depuis des mois…) un article complet sur la démarche dans sa totalité, il faut juste que je trouve le temps de le faire…
Noraline 8 avril 2012
Je viens de vérifier mon référencement sur google et en fait, le problème est toujours d’actualité. En espérant voir un billet à ce sujet prochainement 🙂
Cédric G. 9 avril 2012
Patience Noraline : Google ne corrige pas tout ça de manière instantanée 😉
Cela peut mettre 2 à 3 semaines !
(noname) 16 mai 2012
Bonjour Cédric, merci pour toutes ces informations relatives à la réécriture d’URL. Est-ce que vous avez connaissance d’autres éléments pouvant être à l’origine de contenu dupliqué sur Prestashop (idéalement les dernières versions de la bête) ?
Cédric GIRARD 16 mai 2012
Bonjour
J’y travaille actuellement ! Mais bon pas avant mi juin comme c’est parti car j’ai beaucoup, beaucoup de choses sur le feu…
(ps : j’ai modifié le pseudo, non conforme à mes règles…)
aldo Dell'utri 23 mai 2012
bonjour
est ce utilisable avec la version 1.4 qui apparemment génère encore pas mal de DC ????
aldo Dell'utri 26 mai 2012
bonjour
je viens d’integrer cette modif à mon header prestashop 1.4 et pas de soucis apparent
comment savoir su ca marche correctement
REMBELPBERGT 18 juin 2012
apparement 1.4.8 sembel avori probleme url n double
je vois url rewiting /49-produits….
puis ensuite la meme
comme cela … /category.php?id_category=49
comment faire pour corriger le 1.4.8.??
Cédric GIRARD 19 juin 2012
Bonjour
Est-ce que la seconde ne redirige pas vers la première ? C’est étonnant car en principe c’est géré dans la 1.4.x…
Si ce n’est pas le cas, ll faut effectivement travailler à remédier à cela !
aldo Dell'utri 19 juin 2012
non ce n’est pas geré ,
j’ai encore du duplicat avec 1.4.6
REMBELPBERGT 29 juin 2012
cedric
quel code a inserer dans 1.4.8.2 pour remedier au probeleme dc
aussi le theme new (1.5) est t il compatible avec 14.8.2
est ce ca tourne en mutualisé pro
quand l appel sql pour theme fait 45 appels a la base,
faut t il passer en dédié
car memcached ni ZENDne marche pas sur les mutualisés
ou faut til passer en mutualisé avec ce type de machine:
2 processeurs Opteron 6128 (8 coeurs de 2.1GHz, soit 16 coeurs en tout),
avec 4 disques et 2 SSD, 64 gigas de RAM pour le serveur web
2 processeurs Opteron 2427 (6 coeurs de 2GHz, avec 2 disques et 2 SSD, et 16 gigas de RAM pour le serveur mysql
1 processeur Opteron 4170 (6 coeurs de 2.1GHz),8 gigas de
RAM pour le serveur de fichiers
Cédric GIRARD 29 juin 2012
Bonjour
Je n’ai pas encore eu le temps de me consacrer pleinement aux nouvelles versions, je n’ai donc pas de réponse à apporter concernant la version 1.4.8 (la v1.5 sera à priori identique en matière d’optimisation)
Concernant la machine nécessaire pour faire tourner ce genre de CMS, notamment en mutualisé, cela ne dépend pas du fait que ce soit mutualisé ou pas, mais surtout de l’hébergeur.
Chez Phpnet un mutualisé Premium permet de faire tourner 5 boutiques en parallèle sans AUCUN soucis (avec un très bon trafic). Ce n’est pas (du tout) le cas chez d’autres hébergeurs 😉
Quant aux configurations de dédiés, elles sont à mon humble avis… largement sur-dimensionnées pour faire tourner UNE boutique 🙂
aldo Dell'utri 29 juin 2012
en effet je tourne en mutu chez ovh sans probleme
REMBELPBERGT 4 juillet 2012
oui mais en mutualisé memcached ne marche pas,
comment faire pour gerer le cache en mutualisé?
et mccrypt php extension essentielle?
je trouve le temps d appel a la base super lente chez ov.. pro mutualisé? faut t il ajouter sql privée 256Mo?, apprement je bouffe trop de RAM?
pourtant modules éteind, pas de vente en ligne
qq un reponse?
enfin duplicate content corrigé en 14.8.2?
ou devons adopter methode cedric? code etc
comment faire pour seo, faire paser produits, catégories etc avant autre optimiser home page,
code svp?
merci
je vois aleret rewirting mais rexwiting marche?
aldo Dell'utri 4 juillet 2012
je marche sans memcached.. et sans phpmyscript avec l’option cache normal activé et pas de soucis
sur un mutu perso sql de base ……