WordPress multilingue : le piège Polylang qui crée des pages dupliquées

Cet article vous a aidé ?

Vous recevez une alerte Search Console : « Duplicate, submitted URL not selected as canonical. »
Le trafic baisse. Votre équipe de contenu jure que rien n’a changé. Votre agence SEO vous envoie une feuille de calcul remplie d’URLs presque identiques qui diffèrent seulement par /en/ vs pas de préfixe, ou un mystérieux ?lang=en.

C’est le piège Polylang : le site « fonctionne » pour les humains, mais la surface d’URL explose silencieusement. Les crawlers ne pardonnent pas l’ambiguïté.
Et les caches aiment empirer la situation.

Ce que « pages dupliquées » signifie réellement dans l’univers Polylang

« Pages dupliquées » est un terme surchargé. Dans une configuration Polylang, cela peut signifier au moins quatre choses différentes, et chacune demande une correction distincte.

1) Contenu dupliqué provenant de plusieurs URLs résolvant vers la même page de langue

Exemple : /about/ et /en/about/ servent tous deux de l’anglais. Ou /de/uber-uns/ et /uber-uns/?lang=de.
Les humains cliquent l’un ou l’autre. Google indexe les deux et en choisit un comme canonique, parfois le mauvais.

2) « Entités » dupliquées dans WordPress : pages créées en double

C’est le scénario « nous avons deux pages English intitulées About ». Généralement causé par des importateurs, des page builders ou un flux de traduction qui a créé un nouvel article au lieu de lier une traduction.
C’est plus problématique car ce n’est pas seulement une question d’hygiène d’URL ; c’est l’intégrité des données.

3) Archives de taxonomie et pages de termes dupliquées

Les catégories et balises peuvent se multiplier. Un slug de catégorie traduit peut exister à la fois traduit et non traduit. Pire : le même ID de terme peut apparaître dans plusieurs contextes de langue à cause d’un filtrage linguistique mal configuré.

4) Objets de cache dupliqués qui servent la mauvaise langue sous la bonne URL

C’est le tueur silencieux : /fr/produit/ retourne parfois de l’anglais parce que la clé de cache a ignoré la langue. Ensuite Polylang tente de « corriger » cela avec des redirections.
Résultat : boucles de redirection, canonicals mélangés, et une fête de crawlers non invitée.

La bonne question n’est pas « avons-nous des duplicatas ? » mais « quelle couche duplique : routage, canonicalisation, données ou cache ? »
Diagnostiquez cela en premier. Corriger la mauvaise couche, c’est comment vous vous retrouvez avec un site bilingue qui est aussi cassé des deux côtés.

Comment Polylang crée des duplicatas (mécanismes habituels)

Langue dans l’URL : répertoire, sous-domaine ou paramètre

Polylang prend en charge la négociation de la langue via répertoire d’URL (par ex. /en/), sous-domaine (par ex. en.example.com) ou paramètre (par ex. ?lang=en).
Chacun a des modes d’échec différents.

  • Basé sur les répertoires est généralement le moins mauvais pour le SEO, mais il exige des redirections strictes pour qu’il n’existe qu’une seule forme.
  • Basé sur les sous-domaines complique la mise en cache et le scope des cookies mais isole correctement les langues si c’est bien fait.
  • Basé sur les paramètres est la façon la plus simple de fabriquer des duplicatas, car beaucoup de systèmes traitent les query strings comme optionnelles « même page ». Les crawlers, eux, ne le font pas.

L’ambiguïté de la « langue par défaut »

La plupart des incidents de duplication avec Polylang commencent avec la langue par défaut accessible de deux manières :
/about/ et /en/about/.
Quelqu’un décide que « les deux vont bien ». Ils ne vont pas.

Choisissez-en une. Redirigez l’autre. Puis appliquez-le à la périphérie (Nginx/CDN), pas seulement en PHP où c’est plus lent et plus facile à contourner.

Canonicals et hreflang qui divergent

Les balises canonical disent aux crawlers quelle URL est préférée. hreflang dit aux crawlers comment les variantes langue/région se relient.
Quand elles ne sont pas alignées—par exemple la canonical pointe vers /about/ mais hreflang liste /en/about/—vous avez raconté deux histoires différentes à Google.
Google choisira une troisième histoire.

Sitemaps qui listent les deux formes

Si votre sitemap émet à la fois /en/about/ et /about/ (ou mélange des variantes avec paramètres), vous passez du statut « duplicata possible » à « duplicata invité ».
Les sitemaps sont une déclaration d’intention. Si vous listez des déchets, vous obtenez un indexage de déchets.

Cache qui ignore la langue

Les caches ont besoin d’une clé. Si la langue est décidée par cookie, en-tête ou paramètre de requête, et que vous ne variez pas la clé de cache en conséquence, vous servirez la mauvaise langue.
Polylang peut alors rediriger en fonction de la langue détectée, causant des boucles et des chemins de crawl dupliqués.

Blague #1 : Les caches sont comme des tout-petits — si vous ne définissez pas de règles claires, ils vous donneront joyeusement la mauvaise chose avec une totale confiance.

Faits et contexte qui changent la façon de déboguer

  1. Le noyau WordPress n’a pas été conçu d’abord pour le multilingue. L’internationalisation existe, mais le routage de contenu multilingue est du ressort des plugins, ce qui signifie que la « source de vérité » est fragmentée.
  2. Les balises canonical sont devenues un outil SEO courant en 2009. Beaucoup de comportements SEO de WordPress supposent un canonical unique par objet de contenu ; le multilingue introduit un « canonical par variante ».
  3. hreflang n’est pas un boost de classement ; c’est un indice de désambiguïsation. Si vous le faites mal, vous ne vous contentez pas de perdre un avantage — vous créez de la confusion sur quelle URL appartient à quel index.
  4. Les moteurs de recherche traitent les paramètres de requête comme des URLs séparées sauf preuve du contraire. La négociation de langue basée sur des paramètres est essentiellement une duplication avec une meilleure interface.
  5. Les caches HTTP ignorent généralement les cookies par défaut. Si la sélection de la langue est stockée dans un cookie, votre cache doit explicitement varier sur celui-ci — ou vous devez éviter la sélection de langue basée sur cookie pour les pages cachées.
  6. Les CDN peuvent normaliser les URLs de façons surprenantes. Certaines configurations suppriment ou réordonnent les paramètres de requête, ce qui peut fusionner les langues dans le même objet de cache.
  7. Les robots et les préfetchers ne se comportent pas comme les navigateurs. Ils peuvent ne pas accepter les cookies, ne pas exécuter de JS, et vont explorer à grande échelle les liens de langue alternatifs.
  8. Polylang stocke les relations de langue dans ses propres tables/meta. Si vous migrez, importez ou copiez des posts sans préserver cette cartographie, les traductions deviennent orphelines, et les orphelines sont dupliquées lors des « corrections ».
  9. Les changements de permaliens sont des migrations d’URL. Passer de ?lang= à /en/ n’est pas « un réglage ». C’est un plan complet de redirections, un plan de purge de cache et un plan de réindexation.

Un principe de fiabilité s’applique ici. Une idée paraphrasée souvent attribuée à John Allspaw : les incidents proviennent de travaux normaux qui interagissent de façon inattendue, pas d’une seule personne fautive.
La duplication multilingue est exactement cela : comportement normal de plugins + cache normal + outils SEO normaux = un désordre émergent étrange.

Playbook de diagnostic rapide

Quand quelqu’un dit « Polylang crée des pages dupliquées », il décrit généralement un symptôme vu dans les analytics ou les outils SEO.
Votre travail est de localiser rapidement la couche qui duplique.

Première étape : déterminer si les duplicatas sont au niveau URL ou contenu

  • Plusieurs URLs retournent-elles le même HTML (même langue, même contenu) ? C’est une duplication au niveau URL (redirection/canonical/sitemap/cache).
  • Plusieurs posts/pages WordPress existent-ils dans la même langue avec un contenu similaire ? C’est une duplication au niveau contenu (données/processus/import).

Deuxième étape : vérifier la cohérence canonical + hreflang sur une page affectée

  • La canonical doit pointer vers la forme d’URL préférée pour cette langue.
  • Le jeu hreflang doit être complet, cohérent et auto-référentiel (chaque langue pointe correctement vers elle-même).

Troisième étape : vérifier la variance du cache

  • Si la langue change en fonction d’un cookie/en-tête/paramètre, assurez-vous que les clés de cache varient en conséquence.
  • Vérifiez si le CDN met en cache le HTML pour les utilisateurs non authentifiés et s’il distingue la langue.

Quatrième étape : auditer les redirections pour la langue par défaut

  • Choisissez un seul schéma d’URL canonique pour la langue par défaut et appliquez-le avec des 301.
  • Éliminez les « deux portes » vers le même contenu. Les crawlers utiliseront les deux portes.

Tâches pratiques : commandes, sorties, décisions

Voici des vérifications pratiques que vous pouvez exécuter depuis un shell sur un nœud web ou une bastion avec accès.
Chaque tâche inclut (1) une commande, (2) ce que signifie la sortie, et (3) la décision à prendre.
Adaptez les domaines et chemins à votre environnement.

Task 1: Confirm whether two URLs return identical content

cr0x@server:~$ curl -sS -D- https://example.com/about/ -o /tmp/a.html | sed -n '1,20p'
HTTP/2 200
content-type: text/html; charset=UTF-8
cache-control: public, max-age=600
...
cr0x@server:~$ curl -sS https://example.com/en/about/ -o /tmp/b.html && sha256sum /tmp/a.html /tmp/b.html
e3b0c44298fc1c149afbf4c8996fb924...  /tmp/a.html
e3b0c44298fc1c149afbf4c8996fb924...  /tmp/b.html

Signification : Des hachages identiques suggèrent fortement que le même HTML est servi aux deux URLs. C’est une duplication au niveau URL, pas une duplication éditoriale.

Décision : Choisissez une forme d’URL et redirigez l’autre avec un 301 ; alignez ensuite canonical et sitemap sur la gagnante.

Task 2: Inspect canonical and hreflang on the page

cr0x@server:~$ curl -sS https://example.com/en/about/ | grep -Eo '<link[^>]+(canonical|alternate)[^>]+' | head
<link rel="canonical" href="https://example.com/about/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/a-propos/" />

Signification : La canonical pointe vers /about/ tandis que hreflang self pointe vers /en/about/. Ce décalage est un classique qui embrouille l’indexation.

Décision : Faites en sorte que la canonical soit cohérente avec votre schéma d’URL choisi (probablement /en/about/ si vous préfixez toutes les langues, ou /about/ si la langue par défaut n’est pas préfixée et qu’il n’existe qu’un seul chemin).

Task 3: Follow redirects and see if language enforcement is happening

cr0x@server:~$ curl -sS -I -L https://example.com/about/ | sed -n '1,40p'
HTTP/2 200
content-type: text/html; charset=UTF-8
...

Signification : Pas de redirections. Si /about/ et /en/about/ retournent tous deux 200, vous avez deux URLs indexables.

Décision : Ajoutez une redirection 301 pour l’une des formes, idéalement au niveau Nginx/CDN.

Task 4: Check if cookies change the language and therefore must vary cache

cr0x@server:~$ curl -sS -I https://example.com/about/ | grep -i 'set-cookie'
set-cookie: pll_language=en; path=/; secure; HttpOnly; SameSite=Lax

Signification : Polylang définit un cookie de langue. Si votre cache ne varie pas sur ce cookie, des utilisateurs verront la mauvaise langue.

Décision : Soit (a) évitez le changement de langue piloté par cookie pour les pages cachées en forçant la langue dans l’URL, soit (b) configurez la variation du cache correctement (souvent pénible et coûteux).

Task 5: Verify whether cache varies by cookie/header (response hints)

cr0x@server:~$ curl -sS -I https://example.com/en/about/ | grep -iE 'vary|x-cache|cf-cache-status|age'
vary: Accept-Encoding
x-cache: HIT
age: 531

Signification : Vary ne mentionne pas cookie ni en-tête de langue, et le cache est en HIT. Si la sélection de langue dépend d’un cookie, c’est suspect.

Décision : Corrigez le keying du cache (règles en périphérie) ou réarchitecturez la négociation de langue pour qu’elle soit basée sur l’URL pour le trafic anonyme.

Task 6: Compare HTML language markers between two variants

cr0x@server:~$ curl -sS https://example.com/fr/a-propos/ | grep -Eo '<html[^>]+' | head -n 1
<html lang="en-US">

Signification : Une URL française retournant lang="en-US" suggère fortement un contenu en mauvaise langue ou une mauvaise configuration du thème.

Décision : Traitez cela comme une fuite de cache ou un bug de template ; vérifiez le contexte langage de Polylang et les règles de cache avant de toucher aux réglages SEO.

Task 7: Check whether your sitemap lists duplicates

cr0x@server:~$ curl -sS https://example.com/sitemap.xml | grep -Eo '<loc>[^<]+' | sed 's/<loc>//' | head
https://example.com/about/
https://example.com/en/about/
https://example.com/fr/a-propos/

Signification : Le sitemap inclut explicitement à la fois l’URL anglaise par défaut et la version préfixée.

Décision : Corrigez la génération du sitemap (intégration plugin SEO + Polylang) pour que seules les URLs canoniques soient listées.

Task 8: Search access logs for language parameter crawl storms

cr0x@server:~$ sudo awk '$7 ~ /lang=/ {count++} END {print count}' /var/log/nginx/access.log
18427

Signification : Beaucoup de requêtes incluent lang=. Soit des liens internes fuient les URLs avec paramètres, soit les bots les ont découvertes.

Décision : Cessez de générer des URLs paramétrées, redirigez-les en 301 vers les équivalents en répertoire/sous-domaine, et retirez-les des sitemaps et liens internes.

Task 9: Confirm whether different query strings are cached as the same object

cr0x@server:~$ curl -sS -I "https://example.com/about/?lang=en" | grep -iE 'x-cache|cf-cache-status|age'
x-cache: HIT
age: 590
cr0x@server:~$ curl -sS -I "https://example.com/about/?lang=fr" | grep -iE 'x-cache|cf-cache-status|age'
x-cache: HIT
age: 590

Signification : Même valeur age et pattern HIT suggèrent que le cache ignore peut-être les query strings ou les normalise.

Décision : Corrigez la clé de cache CDN/Nginx pour inclure la query string quand c’est approprié, ou (mieux) éliminez complètement le mode langue via query-string.

Task 10: Validate WordPress sees the correct home URL per request

cr0x@server:~$ wp option get home
https://example.com
cr0x@server:~$ wp option get siteurl
https://example.com

Signification : Les valeurs de base semblent normales. Cette vérification est importante car un home/siteurl dépareillé peut produire des canonicals et redirections mixtes qui paraissent « multilingues ».

Décision : Si ces valeurs diffèrent ou sont incorrectes (http vs https), corrigez-les avant d’incriminer Polylang.

Task 11: Inspect Polylang language configuration quickly

cr0x@server:~$ wp plugin list --status=active | grep -i polylang
polylang                      3.6.2   active
cr0x@server:~$ wp option get polylang
Error: Could not get 'polylang' option. Does it exist?

Signification : Polylang stocke beaucoup d’informations dans ses propres tables et options multiples ; vous ne trouverez pas forcément un seul blob d’option.

Décision : Utilisez l’inspection de la base de données pour les tables Polylang et vérifiez le mode d’URL dans l’UI d’administration ; ne supposez pas que le CLI vous donne toute l’image.

Task 12: Identify duplicate posts by title within a language (content-level duplication)

cr0x@server:~$ wp db query "SELECT p.ID, p.post_title, pm.meta_value AS lang
FROM wp_posts p
JOIN wp_term_relationships tr ON tr.object_id = p.ID
JOIN wp_term_taxonomy tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
JOIN wp_terms t ON t.term_id = tt.term_id
LEFT JOIN wp_postmeta pm ON pm.post_id = p.ID AND pm.meta_key = '_pll_post_language'
WHERE p.post_type='page' AND p.post_status='publish' AND tt.taxonomy='language'
ORDER BY p.post_title LIMIT 10;"
+-----+----------------+------+
| ID  | post_title     | lang |
+-----+----------------+------+
| 311 | About          | NULL |
| 947 | About          | NULL |
| 102 | Careers        | NULL |
+-----+----------------+------+

Signification : Cette sortie est illustrative : le lien de langue n’est pas forcément dans _pll_post_language comme d’autres plugins ; vous pouvez voir NULL selon la version du schéma.
La partie utile est la technique : interroger pour trouver les duplicatas puis vérifier leurs relations de langage Polylang.

Décision : Si vous avez réellement des objets de post dupliqués, corrigez le mapping de traduction (lier les traductions) ou supprimez/redirigez les duplicatas non voulus. Ne tentez pas de « corriger » un problème de données uniquement par des balises canonical.

Task 13: Check Nginx for rewrite rules that create shadow URLs

cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -RIn "rewrite.*lang|return 30[12].*/en/|try_files.*\\$args" /etc/nginx | head
/etc/nginx/sites-enabled/example.conf:47:    rewrite ^/about/$ /en/about/ permanent;
/etc/nginx/sites-enabled/example.conf:63:    try_files $uri $uri/ /index.php?$args;

Signification : Vous pouvez avoir des redirections écrites à la main. Le try_files avec $args préserve les query strings, ce qui peut maintenir vivantes les URLs paramétrées.

Décision : Rendre les redirections cohérentes et agressives : si le mode langue via query-string n’est pas désiré, le supprimer/rediriger. Assurez-vous aussi de ne pas réécrire seulement certains chemins et laisser d’autres dupliqués.

Task 14: Confirm the database doesn’t contain both slug variants for the same language

cr0x@server:~$ wp db query "SELECT post_name, COUNT(*) c FROM wp_posts WHERE post_type='page' AND post_status='publish' GROUP BY post_name HAVING c > 1 ORDER BY c DESC LIMIT 10;"
+-----------+---+
| post_name | c |
+-----------+---+
| about     | 2 |
+-----------+---+

Signification : Deux pages publiées partagent le même slug. WordPress désambiguïse avec des suffixes ou des bizarreries de routage selon la hiérarchie, mais le routage multilingue peut donner l’impression de « pages dupliquées par langue ».

Décision : Corrigez le modèle de contenu : slugs uniques par contexte de langue, et assurez-vous que les traductions sont liées plutôt que dupliquées.

Task 15: Check for mixed language output being served from cache (spot-check)

cr0x@server:~$ for u in /en/about/ /fr/a-propos/; do echo "== $u"; curl -sS https://example.com$u | grep -Eo '<title>[^<]+' | head -n1; done
== /en/about/
<title>About - Example</title>
== /fr/a-propos/
<title>About - Example</title>

Signification : L’URL française retourne un titre en anglais. Ce n’est presque jamais un problème SEO pur. C’est une variance de cache, un mapping de traduction cassé, ou une détection de langue qui retombe sur un fallback.

Décision : Désactivez temporairement le cache full-page pour confirmer, puis corrigez le keying du cache et purgez.

Task 16: Verify headers for canonical host/proto correctness

cr0x@server:~$ curl -sS https://example.com/en/about/ | grep -Eo '<link rel="canonical" href="[^"]+' | head -n1
<link rel="canonical" href="http://example.com/en/about/

Signification : La canonical pointe vers HTTP alors que le site est en HTTPS. Cela crée des « duplicatas » par schéma, et le multilingue ne fait qu’agrandir le graphe.

Décision : Corrigez les réglages d’URL WordPress, les en-têtes du reverse proxy (X-Forwarded-Proto) et la configuration du plugin SEO pour que les canonicals utilisent le schéma public correct.

Modes de défaillance par couche (WordPress, plugins, web, CDN, bots)

Couche WordPress : permaliens et pages hiérarchiques

Le routage WordPress est déterministe jusqu’à ce que vous introduisiez plusieurs URLs « valides » pour le même contenu. Ensuite vous entrez dans le territoire de l’ambiguïté :
pages hiérarchiques, attachments et règles de réécriture auto-générées peuvent donner aux crawlers plusieurs chemins.

Si votre langue par défaut n’est pas préfixée, le permalien de la langue par défaut doit être la seule variante accessible. S’il est accessible à la fois préfixé et non préfixé, vous avez créé une seconde identité.
WordPress ne vous empêchera pas. WordPress est poli comme ça.

Couche Polylang : négociation de langue et mapping des traductions

Polylang est bon dans ce qu’il fait : attacher le contexte de langue, construire les liens alternatifs et permettre de traduire du contenu.
Le piège est de supposer qu’il gouverne aussi le cache, les redirections à la périphérie et le comportement des sitemaps d’autres plugins. Ce n’est pas le cas.

Le mapping des traductions est important. Si une page existe en deux langues mais qu’elles ne sont pas liées comme traductions, Polylang peut les traiter comme des pages indépendantes.
Ensuite un éditeur de bonne foi duplique une page « pour la traduire », et maintenant vous avez deux pages dans la même langue parce que quelqu’un a cliqué au mauvais endroit dans un menu déroulant.

Couche plugin SEO : canonicals, sitemaps et directives robots

La plupart des plugins SEO ont une intégration multilingue, mais ça n’est pas magique. Quand les intégrations échouent, elles échouent silencieusement.
Vous vous retrouvez avec :

  • Des sitemaps qui listent à la fois les URLs par défaut préfixées et non préfixées.
  • Des canonicals qui ignorent le contexte de langue courant.
  • Des liens alternatifs corrects mais pointant vers des URLs non canoniques.

Couche serveur web : redirections, normalisation et préservation des query-strings

Les configs Nginx/Apache préservent souvent les query strings par défaut. C’est normalement correct.
Dans les configurations multilingues, cela peut maintenir les modes langue « morts » en vie pour toujours : ?lang= continue de résoudre et se fait indexer.

Les règles de normalisation peuvent aussi créer des duplicatas : slash final vs pas de slash, majuscule vs minuscule, www vs apex. Multipliez cela par 5 langues et vous avez construit une ferme d’URLs.

Couche CDN : clés de cache et normalisation

Les CDN réduisent la charge et améliorent les performances. Ils rendent aussi les bugs globaux en environ 45 secondes.
Si le CDN met en cache le HTML et que la langue varie selon un cookie ou en-tête, vous devez configurer correctement la clé de cache.
Si vous ne pouvez pas, ne mettez pas en cache le HTML qui varie par cookie. Mettez en cache les assets statiques seulement, ou passez au routage URL-based pour les langues.

Couche bots : comment les crawlers découvrent les duplicatas

Les duplicatas deviennent généralement visibles parce que :

  • Les liens internes exposent les deux variantes (menus, sélecteurs de langue, breadcrumbs, erreurs de balise canonical).
  • Les sitemaps les listent.
  • Les chaînes de redirection les exposent.
  • Les liens externes incluent la forme « incorrecte », et votre site l’accepte sans rediriger.

Ne perdez pas de temps à blâmer « Google qui est stupide ». Si vous permettez deux URLs, les crawlers utiliseront deux URLs. Ce n’est pas un bug ; c’est leur travail.

Trois mini-récits d’entreprise issus des tranchées multilingues

Mini-récit 1 : L’incident causé par une mauvaise hypothèse

Une entreprise B2B de taille moyenne a déployé un site bilingue : anglais par défaut, français ajouté pour un nouveau marché.
Ils ont utilisé Polylang avec des répertoires de langue et ont laissé la langue par défaut accessible à la fois comme / et /en/ « parce que le marketing voulait le look /en/ ».

L’hypothèse : les balises canonical arrangeraient tout. Le plugin SEO choisirait une forme, non ?
Il en a choisi une — parfois. Pour certains templates il émettait la canonical sans /en/. Pour d’autres il l’incluait. La navigation du header pointait vers /en/, le footer vers la forme non préfixée.
Les crawlers ont vu deux graphes de liens internes avec la même autorité.

Le résultat n’a pas été un incendie immédiat. Ce fut une dégradation lente : budget de crawl gaspillé, oscillations d’index, et du contenu apparaissant dans la mauvaise langue pour des recherches de marque.
Puis l’équipe support a remarqué quelque chose d’embarrassant : des clients en France atterrissaient sur des pages anglaises parce que la canonical préférée dans l’index était l’anglais sans répertoire, et Google l’a considérée comme la page principale.

La correction a été ennuyeuse : choisir un schéma d’URL, 301 l’autre, régénérer les sitemaps et purger les caches. Les classements se sont stabilisés en quelques semaines.
La vraie leçon : ne jamais autoriser deux URLs « valides » pour la même variante linguistique. Les canonicals ne remplacent pas une prise de décision claire.

Mini-récit 2 : L’optimisation qui s’est retournée contre eux

Une équipe e-commerce avait des problèmes de performance pendant les campagnes. Quelqu’un a activé le full-page caching au CDN pour tout le trafic anonyme.
Super graphiques. La charge a chuté. Les vitesses ont augmenté. Tout le monde était content.

Deux jours plus tard, le support client rapporte : « Les pages espagnoles affichent parfois l’anglais. » Ce n’était pas aléatoire. La clé de cache CDN ignorait le cookie Polylang et ne variait pas selon Accept-Language.
La première requête pour une URL a « gagné » ; tout le monde a obtenu cette langue mise en cache.

Polylang a essayé de corriger la langue via cookie et a redirigé certains utilisateurs. Ces redirections ont aussi été mises en cache de façon incorrecte.
Le site a développé une nouvelle fonctionnalité : des boucles de redirection qui n’arrivaient que dans une région, derrière un ISP, avec un jeu de cookies donné. Classique.

Le postmortem a été simple. L’optimisation était valide dans un monde monolingue et destructrice en multilingue.
Ils ont annulé le cache HTML, conservé le cache des assets statiques, puis réintroduit le caching HTML seulement après avoir déplacé la sélection de langue entièrement dans l’URL et correctement varié les clés de cache.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une organisation média utilisait Polylang sur six langues. Ils avaient déjà été brûlés, donc ils ont imposé une règle :
chaque variante linguistique doit avoir exactement une URL canonique, et chaque variante non canonique doit rediriger en 301 en un seul saut.

Ils avaient aussi un job programmé qui sondait quelques dizaines d’URLs par langue, vérifiait la cohérence canonical et hreflang, et alertait si :
la canonical ne correspondait pas au chemin de la langue demandé, ou si une cible hreflang renvoyait une chaîne de redirections.

Un vendredi, une mise à jour de thème a changé la génération des liens du sélecteur de langue. Il a commencé à émettre des URLs paramétrées (?lang=) pour certains templates.
La surveillance l’a détecté en moins d’une heure : apparition soudaine de lang= dans les logs et pic de réponses 200 non-canoniques.

Ils ont revert la modification de thème, ajouté une redirection défensive de ?lang= vers la forme en répertoire, purgé les caches et sont passés à autre chose.
Pas de drame, pas de chute SEO. Juste un petit incident. L’ingrédient secret n’était pas du génie ; c’était d’avoir une opinion et de l’appliquer continuellement.

Blague #2 : La meilleure stratégie multilingue ressemble à une bonne rotation on-call — ennuyeuse, documentée, et personne n’en parle aux soirées.

Erreurs courantes : symptômes → cause racine → correction

1) Symptom: /en/ et non-/en/ indexés pour l’anglais

Cause racine : La langue par défaut est accessible via deux formes d’URL ; pas de redirections strictes ; le sitemap inclut les deux.

Correction : Choisir un schéma canonique pour la langue par défaut. Ajouter des redirections 301 pour l’autre. S’assurer que les balises canonical et les sitemaps n’émettent que la forme canonique.

2) Symptom: L’URL française affiche parfois du contenu anglais

Cause racine : La clé de cache manque la dimension langue (cookie/en-tête/query) ; CDN ou fastcgi cache Nginx servent la mauvaise variante.

Correction : Déplacer la négociation de langue vers des répertoires/sous-domaines pour les utilisateurs anonymes ; ou varier le cache selon le cookie/en-tête de langue et confirmer avec des tests curl répétés. Purgez les caches.

3) Symptom: Search Console indique « Alternate page with proper canonical tag » pour des milliers d’URLs

Cause racine : URLs basées sur paramètres (?lang=) ou variantes slash crawlables ; les canonicals pointent ailleurs mais les pages retournent toujours 200.

Correction : 301 les variantes paramétrées vers les URLs canoniques en répertoire ; normaliser les slashs finaux ; retirer les duplicatas du sitemap ; s’assurer que les liens internes n’émettent pas de paramètres.

4) Symptom: avertissements hreflang (pas de balises de retour, codes langue erronés)

Cause racine : Mapping de traduction incomplet, langues supprimées sans nettoyage, ou intégration du plugin SEO qui échoue sur certains templates.

Correction : S’assurer que chaque variante linguistique liste un ensemble hreflang complet incluant l’auto-référence ; corriger les liens de traduction ; vérifier les codes (ex. en, fr) et variantes régionales si utilisées.

5) Symptom: pages d’archives catégorie/tag dupliquées par langue

Cause racine : Mauvaise configuration de traduction des taxonomies ; slugs de termes dupliqués ; archives non filtrées par langue de façon cohérente.

Correction : Décider si les slugs de taxonomie doivent être traduits ; appliquer une approche unique ; veiller à ce que les requêtes d’archive soient filtrées par langue ; rediriger les archives non voulues.

6) Symptom: Après migration de domaines, anciennes URLs langue encore résolues

Cause racine : Règles de redirection trop génériques ; caches contenant de l’HTML ancien ; canonicals mixtes (http/https, www/apex) maintenant des variants anciens vivants.

Correction : Mettre en œuvre des redirections explicites host/schéma ; purger le CDN ; vérifier les canonicals sur des pages représentatives ; contrôler les en-têtes de réponse pour host et schéma corrects.

7) Symptom: Les éditeurs voient plusieurs pages « About » dans la même langue

Cause racine : Le flux de traduction a créé de nouveaux posts sans lier les traductions ; les imports ont dupliqué le contenu ; les templates de page builder ont cloné des objets de contenu.

Correction : Dédupliquer le contenu dans WordPress : lier correctement les traductions, supprimer/fusionner les extras, et mettre en place des garde-fous éditoriaux (rôles, workflow, formation).

8) Symptom: Boucles de redirection lors du changement de langue

Cause racine : Redirections conflictuelles (CDN + Nginx + Polylang), cache servant la mauvaise langue déclenchant une redirection, ou règles slash forcées qui se battent avec les règles de préfixe langue.

Correction : Cartographier la logique de redirection de façon centrale (préférer le edge), réduire les couches de redirection, tester avec curl -I -L, et garantir que le cache sert le bon contenu par URL.

Listes de contrôle / plan étape par étape

Étape par étape : choisir une vérité unique d’URL et l’appliquer

  1. Choisir une stratégie d’URL : répertoires ou sous-domaines. Pour la plupart des sites : répertoires.
  2. Décider du comportement de la langue par défaut : préfixée ou non. Si non préfixée, s’assurer que le préfixe par défaut redirige. Si préfixée, s’assurer que la forme non préfixée redirige vers elle.
  3. Normaliser le schéma et l’hôte : un seul host HTTPS ; rediriger les autres.
  4. Supprimer les URLs basées sur paramètres : 301 vers les équivalents répertoire/sous-domaine canoniques.
  5. Faire correspondre les canonicals : la canonical doit correspondre au schéma d’URL choisi pour cette variante linguistique.
  6. Rendre hreflang cohérent : ensemble complet, codes corrects, auto-référentiel, et cibles doivent renvoyer 200 (pas rediriger).
  7. Corriger la génération du sitemap : seules les URLs canoniques, alternates corrects, pas de variantes paramétrées.
  8. Auditer les liens internes : menus, pieds de page, breadcrumbs, articles liés, sélecteur de langue — aucune forme mixte.
  9. Corriger la mise en cache : s’assurer que le cache varie selon la langue, ou ne mettre en cache que le contenu invariant par langue. Préférer le routage URL-based pour le HTML cacheable anonyme.
  10. Purger agressivement : CDN + cache serveur + cache plugin. Puis valider avec des échantillons curl.
  11. Surveiller : journaliser les requêtes ?lang=, suivre les réponses 200 sur les formes non-canoniques, surveiller la dérive canonical/hreflang après les releases.

Checklist de release (celle que vous suivez vraiment)

  • Pour 5 pages représentatives par langue : confirmer un seul saut vers la canonical et la balise canonical correcte.
  • Confirmer que l’attribut lang dans le HTML correspond à la langue de l’URL.
  • Confirmer que les liens du sélecteur de langue n’utilisent pas de query params.
  • Confirmer que le sitemap contient uniquement les formes d’URL canoniques.
  • Confirmer que les en-têtes de cache sont raisonnables et varient correctement (ou que le caching HTML est désactivé s’il ne peut pas être correctement varié).
  • Rechercher dans les logs des pics soudains de formes non-canoniques (/en/ dupliqué, ?lang=, slashs mélangés).

Checklist d’hygiène des données (prévenir les duplicatas éditoriaux)

  • Définir le workflow de « création de traduction » : toujours créer les traductions via l’UI de liaison de Polylang, pas en copiant/collant de nouvelles pages.
  • Restreindre qui peut publier dans les langues secondaires tant que le processus n’est pas stable.
  • Lancer périodiquement des rapports de titres/slugs dupliqués et les revoir avec les ops de contenu.
  • Avant les imports : tester en staging et vérifier que le mapping de traduction survit à la migration.

FAQ

1) Polylang est-il « mauvais pour le SEO » ?

Non. Polylang est correct. Le piège est de laisser plusieurs formes d’URL résoudre vers le même contenu et de supposer que les canonicals pallieront tout.
Le SEO déteste l’ambiguïté plus que n’importe quel plugin en particulier.

2) La langue par défaut doit-elle être préfixée (/en/) ou non ?

Les deux peuvent fonctionner. Choisissez-en une et appliquez-la strictement.
Si vous voulez une cohérence maximale et moins de cas limites, préfixez tout, y compris la langue par défaut. Si vous voulez des URLs par défaut plus propres, laissez la langue par défaut non préfixée — mais assurez-vous que /en/ redirige partout.

3) Pourquoi vois-je des URLs ?lang= alors que j’utilise des répertoires de langue ?

Généralement un composant de thème, un sélecteur de langue ou un plugin génère des liens paramétrés.
Parfois c’est un comportement de repli quand Polylang ne peut pas résoudre une traduction. Traitez-le comme un bug : les URLs paramétrées doivent 301 vers la forme en répertoire.

4) Puis-je simplement ajouter noindex aux duplicatas ?

Vous pouvez, mais ce n’est rarement la meilleure correction initiale. Si les duplicatas sont accessibles et liés en interne, les crawlers y passeront encore du temps.
Préférez les redirections 301 vers une URL canonique unique. Utilisez noindex seulement quand les redirections ne sont pas possibles (rare) ou pour des cas spéciaux comme des listings filtrés.

5) Mon CDN met en cache le HTML. Comment éviter des pages en langue mélangée ?

Faites de la sélection de langue une partie de l’URL (/fr/, /en/) et configurez la clé de cache pour inclure le chemin complet.
Évitez la négociation de langue basée sur cookie pour le HTML cacheable anonyme. Si vous devez utiliser des cookies, variez explicitement le cache sur ce cookie et testez.

6) Pourquoi Search Console montre des duplicatas après que j’ai corrigé les redirections ?

L’indexation n’est pas instantanée. De plus, vous pouvez encore émettre des duplicatas via le sitemap, des liens internes ou des canonicals.
Confirmez que l’URL non canonique retourne maintenant un 301, et que la page canonique a une balise canonical qui pointe vers elle-même, pas une autre variante.

7) Qu’en est-il des taxonomies traduites — les slugs de catégories doivent-ils être traduits ?

Décidez en fonction de l’audience et de l’échelle. Les slugs traduits peuvent offrir une meilleure UX, mais augmentent la complexité.
Si vous traduisez les slugs de taxonomie, assurez-vous de ne pas exposer aussi des archives non traduites pour la même langue. Une langue, une URL d’archive de terme.

8) Le changement de structure de permaliens provoque-t-il des pages dupliquées ?

Cela peut. Changer la structure des permaliens change l’identité des URLs. Dans une configuration multilingue, cela multiplie la zone d’impact.
Traitez cela comme une migration : mappez ancien → nouveau avec des 301, mettez à jour les sitemaps, purgez les caches, et vérifiez canonical/hreflang après le changement.

9) Quelle est la preuve la plus rapide que le cache est en cause ?

Frappez deux URLs de langue à plusieurs reprises et voyez si le contenu bascule ou si les deux retournent le même <title> ou la même valeur lang=.
Si la désactivation du cache (temporairement) corrige le problème, vous avez un problème de variance de cache, pas Polylang qui « duplique » des pages.

10) Dois-je migrer de Polylang vers un autre plugin multilingue ?

Seulement si votre vrai problème est le workflow ou des fonctionnalités manquantes. Si votre problème est des URLs dupliquées, vous pouvez recréer le même désordre avec n’importe quel plugin.
Corrigez d’abord la vérité d’URL, les canonicals, les sitemaps et la mise en cache. Puis évaluez les outils.

Prochaines étapes pratiques

Le piège Polylang n’est pas un seul bug. C’est le système qui fait exactement ce que vous lui avez permis : plusieurs formes d’URL, canonicals incohérents, et des caches qui ne parlent pas « langue ».
Vous ne résolvez pas cela avec un simple réglage de plugin et beaucoup d’optimisme.

Faites ceci ensuite, dans l’ordre :

  1. Choisir un schéma d’URL canonique par langue (incluant une décision claire sur le préfixe de la langue par défaut).
  2. 301 tout le reste à la périphérie. Un seul saut. Pas de débat.
  3. Faire en sorte que canonical + hreflang racontent la même histoire sur tous les templates.
  4. Corriger les émissions de sitemap pour arrêter d’alimenter les crawlers en duplicatas.
  5. Auditer les clés de cache ; si la langue n’est pas dans l’URL, ne mettez pas en cache le HTML anonyme tant que ce n’est pas le cas.
  6. Mettre en place des garde-fous : un petit script de monitoring, une vérification des logs pour ?lang=, et une checklist de release qui teste le routage multilingue comme si ça comptait — parce que ça compte.

WordPress multilingue peut être stable et rapide. Il ne peut juste pas être vague. Faites d’une URL la vérité, et faites en sorte que tout le reste s’excuse avec un 301.

← Précédent
Installer Windows 11 24H2 sans perdre de fichiers : UEFI, Secure Boot, pilotes, fini
Suivant →
Sauvegarde Windows : les restaurations échouent au pire moment — Construisez un vrai test de restauration

Laisser un commentaire