8 хв читанняТуторіали

301 редирект у .htaccess: правила, порядок виконання, рядки запиту

301 редирект у .htaccess - це один рядок mod_alias. Ось цей рядок, коли замість нього правильним інструментом є RewriteRule, і чому порядок виконання важливіший за позицію рядка.

Marius Voß
DevRel · edge infra
Htaccess 301 редирект, показаний як один рядок mod_alias поруч із правилом mod_rewrite, і порядок виконання між ними

301 редирект у .htaccess - це один рядок:

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

Збережіть це у файлі .htaccess у вашому document root, і воно стане активним негайно. Apache читає файл на кожен запит, тож нема ні перезапуску, ні деплою. Директива походить з mod_alias, який присутній практично на кожній інсталяції Apache, і вона відправляє 301 Moved Permanently із заголовком Location. Це вся робота.

Майже все, що йде не так із редиректами Apache, трапляється вже після цього моменту: сягання за RewriteRule, коли достатньо Redirect, змішування двох модулів в одному файлі й отримання порядку, якого ніхто не очікував, або втрата рядка запиту по дорозі. Цей матеріал охоплює чотири правила, які варто запам'ятати, пастку з порядком виконання, через яку правильні на вигляд файли поводяться неправильно, і те, як перевірити редирект без брехні вашого браузера. Для ширшої картини того, де можуть жити редиректи, дивіться як перенаправити URL.

Один рядок, що охоплює більшість редиректів

Redirect приймає статус, шлях для збігу і ціль. Ціль може бути повним URL або шляхом на тому самому хості:

Redirect 301 /old-page /new-page
Redirect 301 /shop https://shop.example.com/
RedirectMatch 301 ^/blog/([0-9]{4})/(.*)$ /articles/$2

Перед використанням варто знати дві поведінки. По-перше, власні рекомендації Apache прямо кажуть, що це правильний інструмент: "Такий простий редирект одного URL або класу URL кудись в інше місце слід реалізовувати за допомогою цих директив, а не RewriteRule."

По-друге, і це дивує людей: "Пам'ятайте, що Redirect зберігає інформацію про шлях. Тобто редирект для URL /one також перенаправить усі URL під ним, такі як /one/two.html і /one/three/four.html." Якщо ви хочете лише точний шлях, RedirectMatch 301 ^/one$ фіксує його. Інакше ви щойно перенаправили цілісне піддерево, а це іноді саме те, чого ви хотіли, а іноді - дуже заплутаний ранок.

RedirectMatch - це regex-версія, і вона охоплює більшість того, для чого люди відкривають mod_rewrite. Захоплені групи потрапляють у $1, $2 і так далі, тож перейменування директорії чи зміна схеми URL на основі дати вирішуються одним рядком.

Коли вам справді потрібен RewriteRule

Сягайте за mod_rewrite, коли рішення залежить від чогось іншого, ніж шлях. Хост, рядок запиту, метод запиту, cookies і user agent - усе це видно RewriteCond і невидно Redirect:

RewriteEngine On
RewriteCond %{QUERY_STRING} (^|&)ref=oldpartner(&|$)
RewriteRule ^landing$ /partners/oldpartner? [R=301,L]

Є одна пастка в per-directory контексті, яка пояснює величезну частку скопійованих правил, що взагалі нічого не роблять. Apache відсікає префікс директорії до збігу, тож шаблон ніколи не бачить провідного слеша: "Видалений префікс завжди закінчується слешем, а це означає, що збіг відбувається з рядком, який ніколи не має провідного слеша. Тому шаблон з ^/ ніколи не збігається в per-directory контексті."

Саме тому RewriteRule ^/old$ /new [R=301] працює, коли хтось вставляє це у virtual host, і мовчки не працює у .htaccess. Приберіть слеш: ^old$. Коли ваша підстановка - відносний шлях, а rewrite живе в підкаталозі, вам також може знадобитися RewriteBase, щоб повідомити Apache, відносно чого рахувати шляхи.

Пастка порядку виконання

Це та пастка, яка коштує людям цілого дня. Позиція рядка у файлі не визначає, який модуль запускається першим.

Apache документує це прямо: "Якщо ви змішуєте Redirect і RewriteRule в одному контексті, знайте, що порядок їхнього виконання залежить від того, де вони з'являються. У контексті сервера/virtual-host mod_rewrite запускається першим; у per-directory контексті (.htaccess) першим запускається mod_alias."

Прочитайте це двічі, бо наслідок протиінтуїтивний. У файлі .htaccess Redirect в кінці файлу переможе RewriteRule на початку. Перенесіть ідентичні правила у virtual host - і переможець зміниться. Прапорець [L] вас теж не врятує: він означає останнє правило в цьому проході mod_rewrite, а не останнє правило у файлі, і він не має влади над іншим модулем.

Практичне правило, яке я дотримуюсь: один модуль на файл. Якщо проекту хоч десь потрібні умови, робіть усі його редиректи через mod_rewrite і видаляйте рядки Redirect. Саме змішані файли породжують баг-репорти типу "редирект працює на staging, але не на продакшені", бо два середовища ставлять правила в різні контексти.

Порядок виконання mod_alias і mod_rewrite, що показує: у файлі htaccess mod_alias запускається першим незалежно від позиції рядка

Рядки запиту: збережені, замінені або стерті

Маркетингові посилання живуть і вмирають своїми рядками запиту, тож цю таблицю варто закріпити на видному місці. Усе тут - документована поведінка, а не фольклор.

Що ви пишетеОригінальний рядок запитуПримітки
Redirect 301 /a /bПереноситьсяmod_alias додає його за вас
RedirectMatch 301 ^/a$ /bПереноситьсяТой самий модуль, та сама поведінка
RewriteRule ^a$ /b [R=301]Проходить незміннимЗадокументована поведінка за замовчуванням
RewriteRule ^a$ /b?src=x [R=301]Замінюється вашимВаші параметри перемагають
RewriteRule ^a$ /b?src=x [R=301,QSA]Поєднується з вашимQSA додає оригінал
RewriteRule ^a$ /b? [R=301]СтираєтьсяСамотній ? очищає його

Формулювання Apache щодо поведінки за замовчуванням: "За замовчуванням рядок запиту передається без змін." А щодо прапорців, [QSA] "додає будь-який рядок запиту з оригінального URL запиту до будь-якого рядка запиту, створеного в цілі rewrite", тоді як [QSD] відкидає вхідний. Якщо ви робите редирект на абсолютний URI, рядок запиту йде разом, якщо ви не попросили [QSD].

Збій, якому це запобігає, тихий і дорогий. Правило, що замінює рядок запиту, вирізає utm_source і utm_campaign по дорозі, ваша аналітика приписує сесію прямому трафіку, і жодної помилки не виникає. Ніхто не помічає, доки хтось не запитає, чому весняна кампанія не отримала своєї заслуги. UTM-параметри не показуються в GA4 охоплює діагностику з боку аналітики.

Якщо ви ловите себе на тому, що підтримуєте десятки кампанійних редиректів у конфігураційному файлі сервера, - це сигнал, а не рутина. Правила на сервері потребують деплою, перегляду конфігурації Apache і когось із доступом до shell. Перенесіть посилання кампаній на короткі посилання, які можете редагувати самі, а .htaccess залишіть для структурних редиректів, у яких він справді хороший.

HTTPS і www за один перехід, а не за два

Найкопійованіший фрагмент коду в інтернеті робить це у двох блоках правил, а це означає, що відвідувач, який прийшов на http://example.com/page, отримує редирект двічі: раз - щоб додати TLS, раз - щоб додати www. Два переходи, два round trip, і трохи слабший сигнал на кожному.

Одне правило, дві умови, один перехід:

RewriteEngine On
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L]

За CDN чи балансувальником навантаження %{HTTPS} зазвичай off на origin, навіть коли відвідувач на HTTPS, бо TLS завершується вище за течією. Замість цього перевіряйте %{HTTP:X-Forwarded-Proto}:

RewriteCond %{HTTP:X-Forwarded-Proto} !https

Помилка тут - і ви будуєте нескінченний цикл редиректів: проксі відправляє HTTPS, origin думає, що це HTTP, робить редирект на HTTPS, і так по колу, доки браузер не здасться. Як виправити цикл редиректів розкриває діагностику цього за заголовками відповіді.

Два окремі правила htaccess, що створюють ланцюжок редиректів у два переходи, порівняно з одним правилом, що досягає канонічного HTTPS www URL за один перехід

Чому воно не працює

У порядку, в якому я їх перевіряю:

  1. AllowOverride виставлено в None. Документація Apache озвучує поведінку за замовчуванням: "Це означає, що файли .htaccess повністю ігноруються, якщо ви явно не увімкнете їх для директорії." Перевірте це, поставивши навмисне сміття в перший рядок. Відсутність помилки 500 означає, що ваш файл узагалі не читається, і кожне правило в ньому - декорація.
  2. Файл розміщений не там, де треба, або має неправильну назву. Він має бути саме .htaccess, з провідною крапкою, у тій директорії, на яку відображається запит. Редактори, які "допомагають" зберегти його як htaccess.txt, - постійна причина.
  3. mod_rewrite не завантажений. Redirect працює, RewriteRule мовчки не працює, і люди годину шукають помилку у своєму regex.
  4. Ваш браузер закешував старий 301. Chrome і Firefox обидва агресивно кешують постійні редиректи, у межах профілю браузера, тож фікс, який ви щойно задеплоїли, невидимий саме вам, а для всіх інших чудово працює. Під час розробки використовуйте R=302 і перемикайтеся на R=301, коли правило вже правильне.
  5. Правило збігається з власною ціллю. RewriteRule ^(.*)$ /index.php/$1 без захисту - класика. Додайте умову, яка виключає ціль.

Перевіряйте через curl, а не браузер

Одна команда скаже вам статус, ціль і скільки переходів це зайняло:

curl -sIL https://example.com/old-page | grep -iE '^HTTP|^location'

Читайте вивід як послідовність. Два рядки HTTP/2 301 перед 200 означають два переходи, і кожен перехід - справжній round trip для справжнього відвідувача в мобільній мережі. Документація Google про редиректи трактує постійний редирект як найсильніший сигнал для консолідації URL, і дістатися туди за один крок строго краще, ніж за три. Наш перевіряч посилань друкує той самий ланцюжок у браузері, а 301 проти 302 редиректів охоплює, який статус відправляти, коли ви не впевнені.

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

Коли не варто використовувати .htaccess взагалі

Власна позиція Apache однозначна. "Якщо у вас є доступ до основного конфігураційного файлу сервера, вам слід розмістити всю конфігурацію там, а не у файлах .htaccess," бо парсинг на кожен запит коштує реальної роботи: "дозвіл на файли .htaccess спричиняє удар по продуктивності, незалежно від того, чи ви їх насправді використовуєте." На shared-хостингу у вас немає вибору. На сервері, який контролюєте ви, основна конфігурація - краще місце для всього структурного.

Є ще один аргумент тримати правила осторонь від файлу взагалі, і він не має нічого спільного з продуктивністю. Редиректи, що з'являються в друці, в QR-коді чи в чиїйсь презентації, мають переживати ваш веб-сервер, вашу міграцію CMS і, можливо, вашого хостинг-провайдера. Правило в .htaccess - це один неохайний деплой від зникнення, і ніхто не повідомляє про мертве надруковане посилання, доки кампанія не закінчиться. Структурним редиректам місце на сервері; кампанійним і друкованим посиланням - там, де ви можете редагувати їх за секунди й вимірювати без грепання логів доступу.

Читайте наріжну серію

Цей матеріал належить до кластера туторіалів. Для повної карти типи редиректів охоплює кожен код статусу і клієнтський метод, а як перенаправити URL охоплює шість місць, де може жити редирект.

Пов'язане в блозі

Поширені запитання

Як створити 301 редирект у .htaccess?

Додайте один рядок у файл .htaccess у корені вашого document root: Redirect 301 /old-page https://example.com/new-page. Apache читає .htaccess на кожен запит, тож редирект стає активним у момент збереження файлу. Без перезапуску, без деплою. Ця директива походить із mod_alias, який увімкнений практично на кожній інсталяції Apache.

Яка різниця між Redirect і RewriteRule?

Redirect і RedirectMatch походять з mod_alias і роблять одну річ: відправляють код статусу і заголовок Location. RewriteRule походить з mod_rewrite і може перевіряти хост, рядок запиту, cookies чи user agent перед тим, як прийняти рішення. Власна документація Apache каже, що для простого редиректу слід використовувати mod_alias, а не RewriteRule, і трактує mod_rewrite як останній засіб.

Чому мій редирект у .htaccess не працює?

П'ять причин охоплюють майже все: AllowOverride виставлено в None, тож файл повністю ігнорується; файл не в document root або має неправильну назву; mod_rewrite не завантажений; браузер закешував попередній 301 і більше не звертається до сервера; або правило збігається з власною ціллю і зациклюється. Перевіряйте через curl, а не браузер, бо закешований 301 змушує виправлене правило виглядати зламаним.

Чи переживає рядок запиту 301 редирект у .htaccess?

З Redirect і RedirectMatch - так, він переноситься автоматично. З RewriteRule відповідь залежить від підстановки: без знака питання в ній - оригінальний рядок запиту проходить далі, знак питання і власні параметри замінюють оригінал, самотній знак питання в кінці стирає його, а прапорець QSA поєднує обидва. Помилка тут мовчки викидає UTM-параметри.

Як перенаправити HTTP на HTTPS та non-www на www за один перехід?

Використайте одне правило з двома умовами, з'єднаними через OR, яке переписує на канонічну схему та хост за один крок. Два окремі блоки правил створюють два редиректи для кожного, хто заходить на http:// без www, а кожен додатковий перехід коштує затримки й розмиває сигнал. За проксі чи CDN перевіряйте %{HTTP:X-Forwarded-Proto} замість %{HTTPS}, інакше отримаєте цикл.

Чи сповільнює .htaccess сайт?

Трохи, і неминуче. Документація Apache каже про це прямо: дозвіл на файли .htaccess спричиняє удар по продуктивності незалежно від того, використовуєте ви їх чи ні, бо httpd шукає цей файл у кожній директорії на кожен запит. Якщо у вас є доступ до основної конфігурації сервера, ті самі правила краще розмістити там, завантажуючи їх один раз під час старту.

Спробуйте Elido

Вставте URL - отримайте коротке посилання

Без реєстрації. Посилання живе 30 днів. Зареєструйтесь, щоб зберегти назавжди.

Безкоштовно, без реєстрації · 2 на день

Спробуйте Elido

URL-скорочувач із хостингом у ЄС: власні домени, глибока аналітика, відкритий API. Безкоштовний тариф - без кредитної картки.

Теги
htaccess 301 redirect
apache redirect
mod_rewrite
301 redirect
redirect https
url redirect

Читати далі