Перейти до вмісту
Сервер та .htaccess

Редиректи в .htaccess для WordPress: як правильно робити 301

Переносимо URL без втрати позицій: яку директиву редиректу .htaccess обрати у WordPress, де саме мають стояти правила і як усе перевірити, не заблокувавши собі доступ.

Опубліковано

Переносите URL і хочете, щоб стара адреса зберегла позиції? Потрібен 301 у .htaccess: поставлений там, де WordPress його не зітре, написаний правильною директивою і націлений на ту саму адресу, яку WordPress реально віддає. Помиліться в будь-якому з трьох пунктів — отримаєте ланцюжок редиректів, петлю або 500, який закриє вам доступ до wp-admin.

Спершу вибір директиви, потім правило розміщення, потім перевірка — саме в такому порядку.

Яка директива: Redirect, RedirectMatch чи RewriteRule

У грі два модулі Apache, і вони не взаємозамінні.

Redirect (mod_alias) — один відомий шлях на одну адресу. Найпростіше, що працює:

Redirect 301 /old-page/ https://example.com/new-page/

Дві особливості, про які варто знати. Директива зіставляє префікс шляху, а не точний рядок, тож /old-page/ ловить і /old-page/anything/, дописуючи /anything/ до адреси призначення. І вона автоматично переносить рядок запиту.

RedirectMatch (mod_alias) — один регулярний вираз на безліч URL. Так переносять цілий розділ:

RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

Група захоплення (.*) стає $1 в адресі призначення, тож /blog/hello-world/ потрапляє на /articles/hello-world/. Зверніть увагу на провідну скісну риску: шаблони mod_alias зіставляються з повним шляхом URL.

RewriteRule (mod_rewrite) — обов’язкове, коли редирект залежить від умови: хоста, протоколу, рядка запиту, user agent. Ніщо в mod_alias цього перевірити не може.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

У шаблона в покаталоговому .htaccess провідна скісна риска зрізається — тому тут ^(.*)$, а в прикладі з mod_alias було ^/blog/. Плутанина саме в цьому місці — найчастіша причина того, чому скопійоване звідкись правило мовчки нічого не робить.

За замовчуванням беріть mod_alias. До RewriteRule тягніться лише тоді, коли потрібна умова. Менше регулярних виразів — менше способів помилитися.

Де мають стояти правила

Власні редиректи ставляться вище рядка # BEGIN WordPress. Не всередині нього.

# --- custom redirects ---
Redirect 301 /old-page/ https://example.com/new-page/
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Дві незалежні причини:

  1. WordPress переписує свій блок сам. Збереження «Налаштування → Постійні посилання» викликає flush_rewrite_rules(), яка відкидає все між маркерами і генерує заново. Правило, покладене всередину, зникне, щойно хтось відкриє цей екран, — можливо, через місяці, і ніхто не пов’яже дві події.
  2. Порядок вирішує, хто переможе. Блок WordPress завершується загальним правилом, що відправляє кожен запит, який не є файлом і не каталогом, до index.php. RewriteRule, поставлене після нього, не виконається ніколи.

Одне чесне ускладнення: mod_alias і mod_rewrite насправді виконуються не в порядку рядків файлу. Apache обробляє mod_alias на етапі трансляції URL, а покаталоговий mod_rewrite пізніше, на етапі fixup, — тож рядок Redirect може перемогти RewriteRule, що стоїть у файлі вище. Якщо вам знадобилися обидва на шляхах, що перетинаються, оберіть для цього шляху один модуль і лишайтеся в ньому. Розбиратися у сутичці mod_alias проти mod_rewrite не вартує витраченої години.

Скісна риска в кінці адреси

Власний канонічний редирект WordPress додає скісну риску в кінець більшості постійних посилань. Тому ось це:

Redirect 301 /old-page/ https://example.com/new-page

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

Спершу відкрийте адресу призначення в браузері, скопіюйте URL рівно в тому вигляді, в якому він усталився в адресному рядку, і використайте його. Якщо ваша структура постійних посилань закінчується на .html або взагалі без скісної риски — підлаштовуйтеся під це. Універсально правильної відповіді тут немає, є лише «повторюйте те, що віддає WordPress».

Як перевіряти, не заблокувавши собі доступ

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

Спершу зробіть резервну копію файлу. Через SSH:

cp /path/to/wordpress/.htaccess /path/to/wordpress/.htaccess.bak

Якщо сайт віддає 500, просто поверніть копію поверх зламаного файлу — сайт підніметься одразу. Тримайте SFTP-сесію вже відкритою в сусідньому вікні: логінитися з нуля, коли сайт лежить, — саме з цього починається паніка. Врахуйте, що apachectl configtest не розбирає .htaccess, тож він відрапортує про здорову конфігурацію, поки ваш сайт мертвий.

Перевіряйте через curl, а не в браузері. Браузери жорстко кешують відповіді 301 і залюбки покажуть вам учорашній результат:

curl -sI https://example.com/old-page/ | head -n 5

Дивіться на дві речі: у рядку статусу має бути 301, а в заголовку Location: — точна кінцева адреса. Щоб побачити весь ланцюжок, пройдіть по ньому:

curl -sIL https://example.com/old-page/ | grep -E '^(HTTP|Location)'

Більше ніж один рядок HTTP/1.1 301 означає, що ви побудували ланцюжок. Більше ніж приблизно п’ять — що ви побудували петлю, і curl зупиниться й сам вам про це скаже.

Поки не впевнені — використовуйте 302. Відповідь 302 не кешується так само, тож помилка відкочується за секунди. Перемикайтеся на 301, коли curl покаже саме ту адресу, яку ви задумали. Це нічого не коштує і врятувало більше сайтів, ніж будь-яка інша звичка з цієї статті.

Якщо правило начебто взагалі нічого не робить, переконайтеся, що Apache взагалі читає файл. AllowOverride None на каталозі змушує Apache цілком ігнорувати .htaccess, а nginx ігнорує його завжди — перевірте заголовок Server: через curl -I, перш ніж налагоджувати регулярні вирази. Щоб зібрати блок із правильним синтаксисом під вашу версію Apache і шлях встановлення, скористайтеся генератором .htaccess для WordPress.

Коли .htaccess узагалі не потрібен

Якщо редиректів небагато і керувати ними мають самі редактори, плагін редиректів, що зберігає правила в базі, — кращий інструмент: він переживає переїзд на інший хостинг і не вимагає SSH. Розмін тут реальний: щоб віддати редирект, доводиться піднімати PHP, а це повільніше за пряму відповідь Apache.

Використовуйте .htaccess для структурних, постійних переносів — зміна домену, перейменування розділу, примусовий HTTPS чи www. Плагін — для разових редакційних редиректів. Поєднувати обидва підходи нормально, доки ви розумієте, який шар володіє якою адресою: редирект, заданий у двох місцях, — це баг, що чекає на свій невдалий день.

FAQ

Питання

Куди додавати власні редиректи у файлі .htaccess WordPress?

Вище рядка # BEGIN WordPress і в жодному разі не між маркерами. WordPress перегенеровує все, що стоїть усередині цих маркерів, щоразу коли хтось зберігає сторінку «Постійні посилання», тож правило всередині блоку зникає без попередження. Розташування важливе й для порядку: блок WordPress завершується загальним правилом, яке відправляє всі незбіглі запити до index.php.

Що використовувати для 301: Redirect, RedirectMatch чи RewriteRule?

Redirect — коли є один конкретний шлях, RedirectMatch — коли один шаблон покриває багато URL, RewriteRule — коли редирект залежить від умови: хоста, протоколу чи рядка запиту. Redirect і RedirectMatch належать до mod_alias і простіші. RewriteRule належить до mod_rewrite і єдиний уміє перевіряти умови.

Чому мій редирект у .htaccess зациклюється?

Зазвичай тому, що адреса призначення досі підпадає під те саме правило, яке туди й відправило відвідувача, — і правило спрацьовує знову й знову. Друга поширена причина: примусовий HTTPS за балансувальником чи CDN, коли сервер бачить звичайний HTTP на кожному запиті, хоча відвідувач уже на HTTPS. У такому разі перевіряйте заголовок X-Forwarded-Proto.

Чи має значення скісна риска в кінці адреси при редиректі у WordPress?

Так. Канонічний редирект WordPress додає скісну риску в кінець більшості постійних посилань, тому 301 на адресу без неї дає два переходи замість одного. Ланцюжки редиректів усе ще передають сигнали ранжування, але марнують краулінговий бюджет і сповільнюють відвідувача. Вказуйте точний кінцевий URL, який віддає WordPress, разом зі скісною рискою.

Як перевірити редирект у .htaccess, не зламавши сайт?

Запустіть curl із прапорцем «лише заголовки» по старій адресі і прочитайте код відповіді та заголовок Location, перш ніж довіряти браузеру. Браузери агресивно кешують відповіді 301 і покажуть вам застарілий результат. Спершу зробіть копію робочого файлу: синтаксична помилка в .htaccess повертає 500 на всіх сторінках, включно з wp-admin.

Чому після додавання редиректу весь сайт віддає помилку 500?

Одна некоректна директива в .htaccess кладе всі URL усередині цього каталогу, включно з адмінкою. Звичні причини: незакрита обгортка IfModule, відсутній рядок RewriteEngine On або змішаний в одному файлі синтаксис доступу Apache 2.2 і 2.4. Видаліть останній доданий блок і перезавантажте сторінку, щоб це підтвердити.