Як lowercase-редирект ламає індексацію URL з кирилицею та іншими не-ASCII символами

«Переадресовуй усі URL на нижній регістр» — типова порада з технічного SEO. Вона прибирає дублі на кшталт /Page/ і /page/ і для простих латинських адрес працює. Я перевірив її на кириличній адресі, і Googlebot за тиждень так і не дійшов до сторінки, хоча в браузері вона відкривалася без проблем.

Нижче розбираю механіку цієї петлі, показую, як налаштувати lowercase-редирект без неї, і як перевірити свій сайт. Повні дані тесту з логами сервера зібрано в окремій картці тесту.

Як не-ASCII символи потрапляють в адресу

Кирилиця, грузинська чи латиниця з діакритикою не можуть стояти в URL напряму. Браузер переводить кожен символ у байти UTF-8, а кожен байт записує як % і два hex-символи. Літера «т» стає %D1%82, а é стає %C3%A9.

Hex-символи A–F можна писати великими або малими літерами. За RFC 3986 записи %D1%82 і %d1%82 означають одне й те саме. Стандарт радить великі літери, а в розділі про нормалізацію прямо каже приводити до них інші записи.

Перший байт будь-якого не-ASCII символу в UTF-8 має код від C2 до F4. Тому в записі кожного такого символу є хоча б одна hex-літера. Те саме стосується закодованих знаків на кшталт коми %2C чи двокрапки %3A.

Як працює найпростіший lowercase-редирект

Найпростіша реалізація бере сирий рядок запиту й переводить його в нижній регістр. У nginx це змінна $request_uri, яка зберігає адресу саме в тому вигляді, як її надіслав клієнт. Якщо зменшений рядок відрізняється від початкового, правило відповідає 301.

Для латинської адреси без кодування все гаразд: /Page/ стає /page/. З не-ASCII адресами правило поводиться інакше. Ось що воно робить з трьома прикладами:

ЗапитВідповідь сервераЩо вийшло
/%D1%82%D0%B5%D1%85… («технічний», стандартне кодування)301 на /%d1%82%d0%b5%d1%85…Літери ті самі, змінився лише регістр hex
/%D0%A2%D0%B5%D1%85… («Технічний», з великої літери)301 на /%d0%a2%d0%b5%d1%85…Велика «Т» лишилася великою
/caf%C3%A9/301 на /caf%c3%a9/Літери ті самі, змінився лише регістр hex

Таблиця показує дві проблеми одразу. Правило не зменшує кириличні літери, бо вони закодовані, тож своєї мети не досягає. Зате воно міняє регістр hex-символів і створює редирект там, де адреса вже була правильною.

Причинно-наслідковий ланцюжок

Сервер у цій історії лише відповідає редиректом. Петлю замикає краулер, який нормалізує адреси за RFC 3986 перед кожним запитом. Для Googlebot події йдуть так:

  1. Googlebot запитує стандартну адресу /%D1%82… і отримує 301.
  2. У заголовку Location стоїть адреса з малими hex /%d1%82….
  3. Перед наступним запитом Googlebot нормалізує її і знову отримує /%D1%82….
  4. Сервер бачить ту саму адресу й знову відповідає 301.
  5. Після кількох спроб Googlebot зупиняється, так і не отримавши вмісту.

Браузер поводиться інакше. У моєму тесті Chrome узяв адресу з Location як є і не змінив регістр hex. Він зробив один редирект, отримав 200 і показав сторінку, тому людина проблеми не помічає.

Що показав тест

Я відтворив таке правило для однієї сторінки й тиждень збирав логи nginx. Адресу з малими hex я давав Google трьома шляхами: через URL Inspection, через окремий sitemap і через заголовок Location. Ось головні факти:

  • Google запитав сторінку 36 разів, і всі 36 запитів ішли до форми з великими hex;
  • у URL Inspection кожен прохід петлі складався з шести запитів окремо для мобільного й десктопного агента, а кожен візит звичайного Googlebot з трьох;
  • форму з малими hex Google не запитав жодного разу, навіть коли брав адресу з sitemap;
  • Chrome, ClaudeBot і QlyzeBot отримали сторінку з кодом 200.

Таблиця всіх запитів і приклади рядків з логів є в картці тесту.

Як я перевірив, що це справді Google

User-Agent легко підробити, тому IP-адреси я перевірив так, як радить Google. Зворотний DNS для кожної адреси повертає ім’я в домені googlebot.com, а прямий запит до цього імені повертає ту саму IP-адресу. Один запит «bingbot» не пройшов такої перевірки, тож я його не враховував.

Як налаштувати lowercase-редирект безпечно

Сам редирект на нижній регістр не шкідливий. Проблема в тому, що найпростіше правило працює із закодованим рядком. Безпечна реалізація спершу декодує шлях, потім зменшує літери й лише після цього кодує результат назад.

Ось ця логіка мовою JavaScript. Її легко перенести в серверне middleware на іншій мові:

const decoded = decodeURI(path);       // "/Технічний-аудит/"
const lower = decoded.toLowerCase();   // "/технічний-аудит/"
if (lower !== decoded) {
  redirect(301, encodeURI(lower));     // "/%D1%82%D0%B5…", великі hex
}

Функція encodeURI завжди повертає стандартну форму з великими hex-символами. Тому адреса, яка відрізняється лише регістром hex, не отримує редиректу, а адреса з великою кириличною літерою отримує правильний редирект на малу. Якщо в адресах сайту трапляються закодовані службові символи на кшталт %2F, цю логіку треба доповнити: decodeURI їх не декодує, і encodeURI закодує їхній % удруге.

Що звірити після налаштування

Саме правило ще не гарантує, що Googlebot отримає сторінку з першого запиту. Адреса має бути однаковою скрізь, де її бачить пошуковик. Варто звірити кілька речей:

  • canonical, sitemap і внутрішні посилання містять стандартну форму з великими hex;
  • адреса з великими hex відповідає 200 або веде на сторінку з 200 за один крок;
  • правило не чіпає параметри запиту, якщо в них є регістрозалежні значення.

Так кожна сторінка має одну стандартну адресу, і Googlebot отримує її з першого запиту.

Як перевірити свій сайт

Перевірка займає кілька хвилин і не потребує доступу до сервера. Візьми сторінку з кирилицею, діакритикою чи закодованим знаком в адресі. Далі виконай такі кроки:

  1. Запроси її через curl -sI у стандартному кодуванні з великими hex.
  2. Подивись на код відповіді й заголовок Location.
  3. Якщо у відповідь приходить 301, а в Location з’явилися малі hex, сайт має цю проблему.

Наприклад, для моєї тестової сторінки команда curl -sI "https://marko-f.com/%D1%82%D0%B5%D1%85%D0%BD%D1%96%D1%87%D0%BD%D0%B8%D0%B9-%D0%B0%D1%83%D0%B4%D0%B8%D1%82/" повертає 301 і Location з малими hex. Підтвердити проблему допоможуть логи сервера. Якщо Googlebot кілька разів поспіль запитує ту саму адресу й щоразу отримує 301, це та сама петля.

Джерела

Механіка спирається на стандарт URI та документацію Google. Поведінку браузера й краулерів я брав з логів власного сервера. Ось першоджерела, на які посилається текст:

Дані тесту зібрано на сервері marko-f.com з 17 по 24 жовтня 2026 року.

Додати в бажані джерела Google

Матеріали marko-f.com частіше показуватимуться вам у Google Пошуку.