Ви прокидаєтеся від сповіщення Search Console: “Duplicate, submitted URL not selected as canonical.”
Трафік падає. Команда контенту запевняє, що нічого не міняла. SEO-агенція надсилає таблицю зі схожими URL, що відрізняються лише на /en/ проти відсутності префіксу або загадковим ?lang=en.
Це — пастка Polylang: сайт «працює» для людей, але поверхня URL непомітно розростається. Пауки пошукових систем не пробачають неоднозначності.
А кеші люблять зробити ситуацію гіршою.
Що насправді означає «дублікати сторінок» у світі Polylang
«Дублікати сторінок» — це перевантажений термін. У налаштуванні з Polylang він може означати щонайменше чотири різні ситуації, і для кожної потрібне своє виправлення.
1) Дублікати контенту через кілька URL, що повертають ту саму мовну сторінку
Приклад: /about/ та /en/about/ обидва віддають англійську. Або /de/uber-uns/ і /uber-uns/?lang=de.
Люди клікають один із них. Google індексує обидва і обирає один canonical, іноді неправильний.
2) Дублікати «об’єктів» у WordPress: пости створені двічі
Це сценарій «у нас є дві англійські сторінки з назвою About». Зазвичай викликано імпортерами, конструкторами сторінок або робочим процесом перекладу, який створив новий пост замість зв’язування перекладу.
Це складніше, бо питання не лише в URL — це питання цілісності даних.
3) Дублікати архівів таксономій та сторінок термінів
Категорії та теги можуть примножуватися. Може існувати перекладений slug категорії і неперекладений одночасно. Ще гірше: той самий ID терміну може опинитися у видимому вигляді в різних мовних контекстах через неправильне фільтрування мови.
4) Дублікати об’єктів кешу, які повертають неправильну мову під правильним URL
Це — тихий вбивця: /fr/produit/ іноді повертає англійську, бо ключ кешу ігнорував мову. Потім Polylang намагається «виправити» це редиректами.
Результат: цикли редиректів, змішані canonical і вечірка для сканерів, на яку ви не запрошували.
Правильне питання не «чи є у нас дублікати?» а «на якому шарі відбувається дублювання: маршрутизація, canonical, дані чи кеш?»
Діагностуйте це насамперед. Виправлення неправильного шару — шлях до двомовного сайту, який ще й стихійно ламається.
Як Polylang створює дублікати (типові механізми)
Мова в URL: директорія, субдомен або параметр
Polylang підтримує вибір мови через директорію URL (наприклад, /en/), субдомен (наприклад, en.example.com) або параметр (наприклад, ?lang=en).
Кожен підхід має різні режими відмов.
- Директорії зазвичай найменш шкідливі для SEO, але вимагають суворих редиректів, щоб існувала тільки одна форма URL.
- Субдомени ускладнюють кешування та сферу дії кукі, але, якщо зроблено правильно, чисто ізолюють мови.
- Параметри — найпростіший спосіб створити дублікати, бо багато систем трактують query string як необов’язковий «той самий» ресурс. А от сканери — ні.
Невизначеність щодо «мови за замовчуванням»
Більшість інцидентів з дублюванням у Polylang починаються з того, що мову за замовчуванням можна дістати двома шляхами:
/about/ і /en/about/.
Хтось каже «обидва варіанти підходять». Насправді — ні.
Оберіть один. Редиректьте інший. І застосуйте це на краю (Nginx/CDN), а не лише в PHP, де це повільніше і легше оминути.
Несумісність canonicals і hreflang
Теги canonical вказують сканерам, яка URL — преферована. hreflang каже, як зв’язані мовні/регіональні варіанти.
Коли вони суперечать — наприклад, canonical вказує на /about/, а hreflang перелічує /en/about/ — ви розповіли Google дві різні історії.
Google обере третю історію.
Карта сайту, яка містить обидві форми
Якщо ваш sitemap містить і /en/about/, і /about/ (або змішуються варіанти з параметрами), ви піднімаєте проблему з «можливого дублювання» до «запрошеного дублювання».
Sitemap — це декларація намірів. Якщо ви вказуєте сміття, отримаєте сміттєве індексування.
Кешування, що ігнорує мову
Кеші потребують ключа. Якщо мова визначається кукі, заголовком або query-параметром, і ви не додаєте цю змінну в ключ кешу, ви будете віддавати неправильну мову.
Polylang тоді може редиректити на основі виявленої мови, спричиняючи цикли й множинні шляхи сканування.
Жарт №1: кеші як малюки — якщо не встановити чіткі правила, вони з радістю підсунуть вам неправильну річ із повною впевненістю.
Факти та контекст, які змінюють підхід до відлагодження
- Ядро WordPress не було створене з пріоритетом мультимовності. Інтернаціоналізація є, але маршрутизація контенту для кількох мов — це зона плагінів, отже «джерело істини» фрагментоване.
- Теги canonical стали мейнстрімом у SEO в 2009 році. Багата поведінка WordPress щодо SEO припускає один canonical на об’єкт контенту; мультимовність вводить «canonical для кожного варіанта».
- hreflang — це не підвищення ранжування; це підказка для усунення неоднозначності. Якщо ви зробите помилку, то не просто втратите користь — ви створите плутанину, який URL має потрапити в який індекс.
- Пошукові системи трактують query-параметри як окремі URL, якщо не доведено протилежне. Параметрна мовна навігація — практично дублювання з приємнішим інтерфейсом.
- HTTP-кеші за замовчуванням зазвичай ігнорують кукі. Якщо вибір мови зберігається в кукі, ваш кеш повинен явно враховувати його — або уникайте кукі для мовних сторінок із кешуванням.
- CDN можуть нормалізувати URL несподіваними способами. Деякі конфігурації видаляють або переставляють query-параметри, що може звести мови в один об’єкт кешу.
- Роботи та префетчери не поводяться як браузери. Вони можуть не приймати кукі, не виконувати JS і активно сканувати альтернативні мовні посилання.
- Polylang зберігає відношення мов у власних таблицях/мета. Якщо ви мігруєте, імпортуєте або копіюєте пости без збереження цих зв’язків, переклади стануть сиротами, а сироти — дублікованими під час «виправлень».
- Зміни структури постійних посилань — це міграція URL. Перехід з
?lang=на/en/— це не «налаштування». Це повний план редиректів, план очищення кешу і план реіндексації.
Один принцип надійності тут доречний. Перефразована ідея, часто приписувана John Allspaw: інциденти виникають через звичайну роботу, що взаємодіє несподівано, а не через одну погану людину
.
Дублювання в мультимовності — саме таке: звичайна поведінка плагіна + звичайне кешування + звичайні SEO-інструменти = дивний емерджентний хаос.
Швидка інструкція з діагностики
Коли хтось каже «Polylang створює дублікати сторінок», зазвичай описується симптом з аналітики або SEO-інструментів.
Ваше завдання — швидко знайти шар, де відбувається дублювання.
Перше: визначте, чи дублікати на рівні URL або контенту
- Чи повертають кілька URL однаковий HTML (та сама мова, той самий контент)? Це дублювання на рівні URL (редирект/canonical/sitemap/кеш).
- Чи існує кілька записів WordPress з однаковою мовою та схожим контентом? Це дублювання на рівні даних (робочий процес/імпорт).
Друге: перевірте узгодженість canonical + hreflang на одній проблемній сторінці
- Canonical має вказувати на преферований формат URL для цієї мови.
- Набір hreflang має бути повним, консистентним і самопосилальним (кожна мова вказує на себе правильно).
Третє: перевірте варіацію кешу
- Якщо мова змінюється через кукі/заголовок/query-параметр, переконайтесь, що ключ кешу враховує це.
- Перевірте, чи CDN кешує HTML для незалогінених користувачів і чи розрізняє він мови.
Четверте: аудит редиректів для мови за замовчуванням
- Оберіть одну канонічну схему URL для мови за замовчуванням і застосуйте 301.
- Ліквідуйте «два входи» до того самого контенту. Сканери використовуватимуть обидва входи.
Практичні завдання: команди, виводи, рішення
Це практичні перевірки, які можна виконати з shell на веб-ноді або бастіоні з доступом.
Кожне завдання містить (1) команду, (2) що означає її вивід, і (3) рішення, яке потрібно прийняти.
Підлаштуйте домени та шляхи під ваше середовище.
Завдання 1: Підтвердити, чи два URL повертають ідентичний контент
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
Значення: Ідентичні хеші сильно вказують, що один і той же HTML віддається за обома URL. Це дублювання на рівні URL, а не редакційне дублювання.
Рішення: Оберіть один формат URL і зробіть 301 для другого; потім вирівняйте canonical і sitemap під переможця.
Завдання 2: Перевірити canonical і hreflang на сторінці
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/" />
Значення: Canonical вказує на /about/, тоді як hreflang self вказує на /en/about/. Це класична проблема індексації.
Рішення: Узгодьте canonical із вибраною схемою URL (ймовірно /en/about/, якщо ви префіксуєте всі мови, або /about/, якщо мова за замовчуванням без префіксу і існує лише один шлях).
Завдання 3: Прослідкуйте редиректи і перевірте, чи відбувається примусова мовна логіка
cr0x@server:~$ curl -sS -I -L https://example.com/about/ | sed -n '1,40p'
HTTP/2 200
content-type: text/html; charset=UTF-8
...
Значення: Нема редиректів. Якщо /about/ і /en/about/ обидва повертають 200, у вас є два індексовані URL.
Рішення: Додайте 301 для однієї з форм, бажано на рівні Nginx/CDN.
Завдання 4: Перевірити, чи кукі змінюють мову і тому мають вплив на кеш
cr0x@server:~$ curl -sS -I https://example.com/about/ | grep -i 'set-cookie'
set-cookie: pll_language=en; path=/; secure; HttpOnly; SameSite=Lax
Значення: Polylang встановлює кукі мови. Якщо ваш кеш не варіює за цією кукі, користувачі можуть бачити неправильну мову.
Рішення: Або (a) уникати кукі-керованої мовної логіки для кешованих сторінок, навівши мову в URL, або (б) правильно сконфігурувати варіацію кешу (часто болісно й дорого).
Завдання 5: Перевірити, чи кеш варіює за кукі/заголовком (ознаки у відповідях)
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
Значення: Vary не згадує cookie або заголовок мови, і кеш повертає HIT. Якщо вибір мови залежить від кукі, це підозріло.
Рішення: Виправити ключ кешу (правила на краю) або переглянути архітектуру мовної навігації — використовуйте URL-базовану логіку для анонімного HTML.
Завдання 6: Порівняти HTML-позначки мови між двома варіантами
cr0x@server:~$ curl -sS https://example.com/fr/a-propos/ | grep -Eo '<html[^>]+' | head -n 1
<html lang="en-US">
Значення: Французький URL повертає lang="en-US", це чіткий сигнал про неправильну мову в контенті або помилку в шаблоні.
Рішення: Сприймайте це як просочування кешу або баг шаблону; перевірте контекст мови Polylang і правила кешу перед тим, як чіпати SEO-настройки.
Завдання 7: Перевірити, чи sitemap містить дублікати
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/
Значення: Sitemap явно включає і префіксований, і непрефіксований англійський URL.
Рішення: Виправте генерацію sitemap (плагін SEO + інтеграція Polylang), щоб у ньому були лише канонічні URL.
Завдання 8: Пошук в логах доступу штурмів з параметром мови
cr0x@server:~$ sudo awk '$7 ~ /lang=/ {count++} END {print count}' /var/log/nginx/access.log
18427
Значення: Багато запитів містять lang=. Або внутрішні посилання «витікають» параметризовані URL, або боти їх виявили.
Рішення: Припиніть генерувати параметрні URL, 301 їх до директорій/субдоменів, і видаліть їх із sitemap та внутрішніх посилань.
Завдання 9: Підтвердити, чи різні query string кешуються як один об’єкт
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
Значення: Та сама age і схема HIT вказують, що кеш може ігнорувати query string або нормалізовувати їх.
Рішення: Виправіть ключ CDN/Nginx, щоб включити query string, де потрібне, або (краще) усуньте режим мови через query string.
Завдання 10: Перевірити, чи WordPress бачить правильний home URL для кожного запиту
cr0x@server:~$ wp option get home
https://example.com
cr0x@server:~$ wp option get siteurl
https://example.com
Значення: Базові налаштування виглядають нормальними. Ця перевірка важлива, бо невідповідність home/siteurl може створювати змішані canonical і редиректи, що виглядають «багатомовно».
Рішення: Якщо вони різняться або неправильні (http vs https), виправте їх до того, як звинувачувати Polylang.
Завдання 11: Швидко перевірити конфігурацію Polylang
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?
Значення: Polylang зберігає багато в власних таблицях і кількох опціях; ви не обов’язково знайдете один чистий blob опцій.
Рішення: Використовуйте інспекцію бази даних для таблиць Polylang і перевірте режим URL в адмінці; не припускайте, що CLI покаже всю картину.
Завдання 12: Знайти дублікати постів за заголовком у межах мови (дублювання даних)
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 |
+-----+----------------+------+
Значення: Цей вивід ілюстративний: зв’язок мови може бути не в _pll_post_language, залежно від схеми/версії. Корисна частина — техніка: шукати дублікати і перевіряти зв’язки Polylang.
Рішення: Якщо у вас дійсно є дублікати об’єктів, виправте зв’язки перекладів (link translations) або видаліть/перенаправте непотрібні дублікати. Не намагайтесь вирішити проблему лише через canonical.
Завдання 13: Перевірити Nginx на предмет правил перезапису, що створюють тіньові URL
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;
Значення: У вас можуть бути ручні редиректи. try_files з $args зберігає query string, що може підтримувати параметризовані мовні URL живими.
Рішення: Зробіть редиректи послідовними і агресивними: якщо режим з query-string не потрібен, прибирайте/редиректьте його. Також переконайтесь, що ви не переписуєте лише деякі шляхи, залишаючи інші дубльованими.
Завдання 14: Підтвердити, що база не містить обох варіантів slug для тієї самої мови
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 |
+-----------+---+
Значення: Дві опубліковані сторінки мають однаковий slug. WordPress розрізняє їх суфіксами або через маршрутизацію залежно від ієрархії, але мультимовна маршрутизація може зробити це схожим на «дублікати мовних сторінок».
Рішення: Виправте модель контенту: унікальні slug для кожного мовного контексту і забезпечте зв’язування перекладів, а не дублювання.
Завдання 15: Перевірити змішаний мовний вивід, що повертається з кешу (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>
Значення: Французький URL повертає англійський title. Це майже ніколи не «SEO»: це проблема варіації кешу, зламаного зв’язування перекладів або резервного виявлення мови.
Рішення: Тимчасово відключіть full-page cache, щоби підтвердити, потім виправте ключування кешу і очистіть кеш.
Завдання 16: Перевірити заголовки щодо коректності канонічного хоста/протоколу
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/
Значення: Canonical вказує на HTTP, тоді як сайт на HTTPS. Це створює «дублікати» за схемою, а мультимовність лише розширює граф.
Рішення: Виправте налаштування WordPress URL, заголовки reverse proxy (X-Forwarded-Proto) і конфігурацію SEO-плагіна, щоб canonical використовував публічну схему.
Режими відмов по рівнях (WordPress, плагіни, веб, CDN, боти)
Рівень WordPress: пермалінки та ієрархічні сторінки
Маршрутизація WordPress детермінована до того моменту, поки ви не введете кілька «валідних» URL для того самого контенту. Тоді виникає зона неоднозначності:
ієрархічні сторінки, вкладення та автогенеровані rewrite правила можуть дати сканерам кілька шляхів.
Якщо ваша мова за замовчуванням не має префіксу, то permalink для мови за замовчуванням має бути єдиним доступним варіантом. Якщо він доступний і в префіксованому, і в непефіксованому вигляді — ви створили другу ідентичність.
WordPress вас не зупинить. WordPress такий чемний.
Рівень Polylang: погодження мови та зв’язування перекладів
Polylang добре виконує свою роботу: встановлює мовний контекст, будує альтернативні посилання і дозволяє перекладати контент.
Пастка — в припущенні, що він також керує кешуванням, редиректами на кромці та поведінкою sitemap інших плагінів. Ні.
Важливе значення має зв’язування перекладів. Якщо сторінка існує в двох мовах, але вони не зв’язані як переклади, Polylang може трактувати їх як незалежні сторінки.
Потім редактор, бажаючи «перекласти», дублює сторінку, і в підсумку у вас є дві сторінки однієї мови через помилку у виборі в випадаючому меню.
Рівень SEO-плагіна: canonical, sitemap і директиви robots
Більшість SEO-плагінів мають інтеграцію з мультимовністю, але це не чарівництво. Коли інтеграції ламаються, вони ламаються мовчки.
Ви отримаєте:
- Sitemap, що містить і префіксовані, і непефіксовані URL мови за замовчуванням.
- Canonical, що ігнорує поточний мовний контекст.
- Альтернативні посилання, що коректні, але вказують на неканонічні URL.
Веб-серверний рівень: редиректи, нормалізація і збереження query-string
Конфіги Nginx/Apache часто зберігають query string за замовчуванням. Це зазвичай правильно.
У мультимовних налаштуваннях це може вічно підтримувати «мертві» мовні режими: ?lang= продовжує працювати і індексується.
Правила нормалізації також можуть створювати дублікати: слеш у кінці vs без слеша, uppercase vs lowercase, www проти apex. Помножте це на 5 мов — і ви отримали ферму URL.
Рівень CDN: ключі кешу і нормалізація
CDN знижують навантаження і прискорюють роботу. Вони також роблять баги глобальними за 45 секунд.
Якщо CDN кешує HTML, а мова залежить від кукі чи заголовка, потрібно налаштувати ключ кешу правильно.
Якщо не можете — не кешуйте HTML, який залежить від кукі. Кешуйте лише статичні ресурси або перейдіть на URL-базовану маршрутизацію мов.
Рівень ботів: як сканери знаходять дублікати
Дублікати зазвичай проявляються, бо:
- Внутрішні посилання видають обидва варіанти (меню, перемикач мов, хлібні крихти, помилки в canonical).
- Sitemap їх перелічує.
- Ланцюги редиректів відкривають їх.
- Зовнішні посилання містять «неправильну» форму, і ваш сайт її приймає без редиректу.
Не витрачайте час на звинувачення «Google тупий». Якщо ви дозволяєте два URL, сканери використовуватимуть два URL. Це не баг; це їхня робота.
Три корпоративні історії з багатомовних «фронтів»
Історія 1: Інцидент через хибне припущення
Середня B2B компанія запустила двомовний сайт: англійська за замовчуванням, додали французьку для нового ринку.
Вони використали Polylang з директоріями мов і дозволили мові за замовчуванням бути доступною як / і /en/ «бо маркетинг хотів вигляд /en/».
Припущення: canonical вирішить проблему. SEO-плагін обере один, чи не так?
Він іноді обирав. Для деяких шаблонів canonical виводився без /en/, для інших — з ним. Хедер навігації вів на /en/, футер — на непефіксований.
Отже, сканери бачили два внутрішні графи посилань з однаковим авторитетом.
Результат не був миттєвим. Це була повільна деградація: більше бюджету сканування, коливання індексації і контент у неправильній мові для брендованих запитів.
Потім служба підтримки помітила неприємну річ: клієнти у Франції потрапляли на англомовні сторінки, бо «переважним» в індексі виявився непефіксований англійський URL.
Виправлення було нудним: обрати одну схему URL, 301 іншу, згенерувати sitemap заново і очистити кеші. Ранжування стабілізувалося за кілька тижнів.
Головний урок: ніколи не дозволяйте двом «валідним» URL для тієї самої мовної варіації. Canonical — не привід бути нерішучим.
Історія 2: Оптимізація, що повернулася бумерангом
Команда e‑commerce мала проблеми продуктивності під час кампаній. Хтось увімкнув кешування сторінок на CDN для всього анонімного трафіку.
Графіки стали прекрасні. Навантаження впало. Швидкість сторінок покращилась. Усі святкували.
Через два дні служба підтримки повідомила: «іспанські сторінки випадково відображають англійські». Це не випадковість. Ключ кешу CDN ігнорував кукі Polylang і не варіював за Accept-Language.
Перший запит до URL «вигравав» — і всі інші отримували кешовану мову.
Polylang намагався коригувати мову через кукі і редиректував деяких користувачів. Ті редиректи теж кешувалися неправильно.
Сайт розвинув нову «фічу»: цикли редиректів, що траплялися лише в одному регіоні, за одним ISP і з певним набором кукі. Класика.
Розслідування було простим. Оптимізація була слушною для одномовного світу і руйнівною для багатомовного.
Вони відкотили кешування HTML, залишили кеш статичних ресурсів, а потім повернули кеш HTML лише після переведення вибору мови у URL і налаштування варіації ключів кешу.
Історія 3: Нудна, але правильна практика, що врятувала становище
Медіаорганізація використовувала Polylang на шести мовах. Їх уже кілька разів «обпікали», тож вони ввели правило:
кожен мовний варіант повинен мати точно один канонічний URL, а всі неканонічні варіанти мають 301 в один крок.
Вони також мали розкладну задачу, яка вибірково перевіряла декілька десятків URL на мову, перевіряла узгодженість canonical і hreflang та алертувала, якщо:
canonical не відповідав мовному шляху запиту або якщо ціль hreflang повертала ланцюг редиректів.
Одної п’ятниці оновлення теми змінило генерацію посилань перемикача мов. Воно почало виводити параметризовані URL (?lang=) для деяких шаблонів.
Моніторинг сповістив за годину: раптове з’явлення lang= в логах і сплеск неканонічних 200 відповідей.
Вони відкотили зміну теми, додали захисний редирект з ?lang= у директорію і очистили кеші.
Жодної драми і жодного SEO-обриву. Просто невеликий спалах. Секрет не в генії, а в наявності чітких правил і їх постійному застосуванні.
Жарт №2: найкраща мультимовна стратегія як хороший on-call — нудна, задокументована, і її не обговорюють на вечірках.
Поширені помилки: симптом → причина → виправлення
1) Симптом: /en/ і непефіксований URL обидва індексуються для англійської
Причина: Мова за замовчуванням доступна в двох формах URL; немає суворих редиректів; sitemap включає обидва.
Виправлення: Оберіть одну канонічну схему для мови за замовчуванням. Додайте 301 редиректи для іншої. Переконайтесь, що canonical і sitemap видають лише обрану схему.
2) Симптом: французький URL іноді показує англійський контент
Причина: Ключ кешу не містить вимір мови (кукі/заголовок/параметр); CDN або Nginx fastcgi cache віддає неправильний варіант.
Виправлення: Перенесіть визначення мови в URL (директорії/субдомени) для анонімних користувачів; або варіюйте кеш за кукі/заголовком і перевіряйте через curl. Очистіть кеши.
3) Симптом: Search Console показує «Alternate page with proper canonical tag» для тисяч URL
Причина: Параметризовані URL (?lang=) або варіанти зі слешем доступні для сканування; canonical вказують інше, але сторінки все ще повертають 200.
Виправлення: Робіть 301 з параметрних варіантів до канонічних директорій; нормалізуйте слеші; видаліть дублікати з sitemap; переконайтесь, що внутрішні посилання не генерують параметри.
4) Симптом: попередження hreflang (немає взаємних тегів, неправильні коди)
Причина: Неповне зв’язування перекладів, видалені мови без прибирання або збої інтеграції SEO-плагіна в деяких шаблонах.
Виправлення: Переконайтесь, що кожен мовний варіант містить повний набір hreflang включно з self; виправте зв’язки перекладів; перевірте коди (en, fr) і регіональні варіанти, якщо застосовуються.
5) Симптом: дублікати архівів категорій/тегів на кожну мову
Причина: Неправильна конфігурація перекладу таксономій; дубльовані slug термінів; архіви не фільтруються за мовою послідовно.
Виправлення: Визначте, чи перекладати slug таксономій; наведіть одну стратегію; забезпечте фільтрацію архівів за мовою; редиректьте непотрібні архіви.
6) Симптом: після міграції доменів старі мовні URL все ще працюють
Причина: Правила редиректів надто загальні; кеши зберігають старий HTML; змішані canonical (http/https, www/apex) підтримують старі варіанти.
Виправлення: Здійсніть явні редиректи хоста/схеми; очистіть CDN; перевірте canonical на репрезентативних сторінках; перевірте заголовки відповіді на предмет правильного хоста і схеми.
7) Симптом: редактори бачать кілька сторінок «About» в одній мові
Причина: Робочий процес перекладу створив нові пости без зв’язування; імпорти продублювали контент; шаблони конструктора сторінок клонували об’єкти.
Виправлення: Видуплікуйте контент у WordPress: зв’яжіть переклади, видаліть/злийте зайві, і впровадьте редакційні правила (ролі, процеси, навчання).
8) Симптом: цикли редиректів при перемиканні мов
Причина: Конфліктні редиректи (CDN + Nginx + Polylang), кеш віддає неправильну мову, яка викликає редирект, або правила слешів борються з префіксами мов.
Виправлення: Централізувати логіку редиректів (на кромці), зменшити кількість шарів редиректів, тестувати з curl -I -L і переконатися, що кеш віддає правильний контент для відповідного URL.
Чек-листи / покроковий план
Покроково: оберіть одну URL-істину і забезпечте її дотримання
- Обрати стратегію URL: директорії або субдомени. Для більшості сайтів — директорії.
- Визначити поведінку мови за замовчуванням: з префіксом чи без. Якщо без префіксу — зробіть 301 з префіксованого. Якщо з префіксом — робіть редирект непефіксованого в префіксований.
- Нормалізувати схему і хост: один HTTPS хост; редиректьте інші.
- Вилучити параметризовані мовні URL: 301 їх у канонічні директорії/субдомени.
- Узгодити canonical: canonical має відповідати обраній схемі URL для кожного мовного варіанта.
- Зробити hreflang консистентним: повний набір, правильні коди, self-посилання; цілі повинні повертати 200 (не редирект).
- Виправити генерацію sitemap: лише канонічні URL, коректні альтернативи, без параметрних варіантів.
- Аудит внутрішніх посилань: меню, футери, хлібні крихти, пов’язані пости, перемикач мови — ні змішаних форм.
- Виправити кешування: переконайтесь, що кеш варіює за мовою, або кешуйте лише контент, що не залежить від мови. Для анонімного HTML краще URL-базована мовна маршрутизація.
- Агресивно очистити: CDN + серверний кеш + плагін-кеш. Потім перевірити вибірковими curl-запитами.
- Моніторити: логувати запити з
?lang=, відстежувати 200 відповіді по неканонічним формам, слідкувати за дрейфом canonical/hreflang після релізів.
Чек-лист релізу (той, що ви дійсно виконуєте)
- Для 5 репрезентативних сторінок на кожну мову: перевірити один редирект до канонічного і коректний тег canonical.
- Перевірити, що атрибут
langв HTML відповідає мові в URL. - Переконатися, що перемикач мов не використовує query params.
- Перевірити, що sitemap містить лише канонічні форми URL.
- Перевірити заголовки кешування — вони адекватні і варіюють за необхідністю (або кеш для HTML вимкнено, якщо варіювати неможливо).
- Шукати в логах раптові сплески неканонічних форм (
/en/дублювання,?lang=, змішані слеші).
Чек-лист гігієни даних (щоб запобігати редакційним дублікатам)
- Визначити робочий процес створення перекладів: завжди створювати переклади через UI зв’язування Polylang, а не копіювати/вставляти нові сторінки.
- Обмежити, хто може публікувати на вторинних мовах, поки процес не стане стабільним.
- Періодично запускати звіти про дублікати заголовків/slug і переглядати їх з командою контенту.
- Перед імпортом: тестувати на staging і перевіряти, чи зберігаються зв’язки перекладів під час міграції.
FAQ
1) Чи Polylang «поганий для SEO»?
Ні. Polylang — нормальний плагін. Пастка — дозволити множинним формам URL резолвитися до одного контенту і думати, що canonical все виправить.
SEO більше ненавидить неоднозначність, ніж будь-який конкретний плагін.
2) Чи повинна мова за замовчуванням мати префікс (/en/) чи ні?
Обидва варіанти працюють. Оберіть один і суворо його дотримуйтеся.
Якщо хочете максимальної послідовності і менше крайових випадків — префіксуйте все, включно з мовою за замовчуванням. Якщо бажаєте «гарніших» URL для мови за замовчуванням — залиште її без префіксу, але переконайтесь, що /en/ скрізь редиректиться.
3) Чому я бачу ?lang= URL, хоча використовую директорії мов?
Зазвичай компонент теми, перемикач мов або плагін генерує посилання з параметрами.
Іноді це fallback, коли Polylang не може знайти переклад. Сприймайте це як баг: параметризовані URL мають 301 до директорій.
4) Чи можна просто додати noindex до дублікатів?
Можна, але це рідко найкращий перший крок. Якщо дублікати доступні і пов’язані внутрішньо, сканери витрачатимуть на них час.
Надавайте перевагу 301 редиректам до одного канонічного URL. Використовуйте noindex лише коли редиректи неможливі (рідко) або для спеціальних випадків як фільтровані підсумки.
5) Мій CDN кешує HTML. Як уникнути змішаних мовних сторінок?
Робіть вибір мови частиною URL (/fr/, /en/) і налаштуйте ключ кешу включно з повним шляхом.
Уникайте кукі‑керованої мовної логіки для кешованого анонімного HTML. Якщо кукі необхідні, явно варіюйте кеш за цією кукі і ретельно тестуйте.
6) Чому Search Console показує дублікати після виправлення редиректів?
Індексування не миттєве. Також ви можете і далі емінувати дублікати через sitemap, внутрішні посилання або canonical.
Переконайтесь, що неканонічний URL повертає 301, а сторінка-канонікаль має canonical, що вказує на себе, а не на інший варіант.
7) Щодо перекладених таксономій — чи слід перекладати slug категорій?
Вирішуйте залежно від аудиторії і масштабу. Перекладені slug можуть бути кращими для UX, але ускладнюють систему.
Якщо ви перекладаєте slug таксономій, переконайтесь, що не експонуєте неперекладені архіви для тієї самої мови. Одна мова — один URL архіву.
8) Чи викликає зміна структури пермалінків дублікати сторінок?
Може. Зміна структури пермалінків змінює ідентичність URL. У мультимовних налаштуваннях це множить радіус ураження.
Розглядайте це як міграцію: зіставте старі → нові з 301, обновіть sitemap, очистіть кеши і перевірте canonical/hreflang після зміни.
9) Який найшвидший доказ того, що кеш — винуватець?
Бийте по двох мовних URL повторно і дивіться, чи контент переключається або чи обидва повертають той самий <title> або значення lang=.
Якщо тимчасове відключення кешу вирішує проблему, то у вас проблема варіації кешу, а не те, що Polylang «створює сторінки».
10) Чи варто переходити з Polylang на інший плагін?
Тільки якщо ваша реальна проблема — робочий процес або відсутні функції. Якщо проблема в дубльованих URL, ви можете відтворити ту саму плутанину з будь-яким плагіном.
Спочатку виправте істину URL, canonical, sitemap і кешування. Потім оцініть інструменти.
Практичні наступні кроки
Пастка Polylang — це не одиночний баг. Це система, яка робить саме те, що ви їй дозволили: множинні форми URL, неконсистентні canonical і кеши, що не розуміють «мову».
Ви не вирішите це перемикачем у плагіні й оптимізмом.
Зробіть наступне, по порядку:
- Оберіть одну канонічну схему URL для кожної мови (включно з чітким рішенням щодо префіксу мови за замовчуванням).
- 301 усе інше на краю. Один крок. Без дискусій.
- Зробіть canonical і hreflang консистентними у всіх шаблонах.
- Виправте генерацію sitemap, щоб припинити постачати дублікати сканерам.
- Аудит ключів кешу; якщо мова не в URL, не кешуйте анонімний HTML, допоки не виправите це.
- Впровадьте захисні заходи: невеликий моніторинговий скрипт, лог-перевірка на
?lang=і релізний чек-лист, що тестує мультимовну маршрутизацію як важливу частину релізу — бо це так і є.
Багатомовний WordPress може бути стабільним і швидким. Він просто не може бути розмитим. Зробіть один URL істинним, і нехай інші вибачаються через 301.