301 редирект через .htaccess: готові правила

301 редирект — це відповідь сервера, яка каже браузеру й пошуковому роботу: сторінка переїхала назавжди, ось її нова адреса. Від інших способів «перекинути» відвідувача він відрізняється тим, що переносить в індекс не лише адресу, а й накопичену вагу посилань. Нижче — готові правила для .htaccess під типові задачі, їхні аналоги для nginx і розбір помилок, через які переїзд коштує сайту трафіку.

Як це працює

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

Браузер або роботGET /old-page/запитСервер301 Moved PermanentlyLocation: /new-page/правило з .htaccessперехідНова адреса200 OK, сторінку відкритоЩо важливо для пошуку301 каже «адреса змінилася назавжди»: пошуковик переносить сторінку в індексі разом із накопиченою вагою посилань.302 означає «тимчасово» — стара адреса лишається в індексі, і вага нікуди не переходить.
Ланцюжок запиту: сервер відповідає 301 і передає нову адресу

Різниця між кодами важлива й коштує грошей:

Код Що означає Коли застосовувати
301 Переїхало назавжди Зміна домену, адреси сторінки, склейка дзеркал, перехід на HTTPS
302 Тимчасово Акція на пару тижнів, техроботи, A/B-тест
307 Тимчасово, метод запиту зберігається Те саме, але для POST-запитів
308 Назавжди, метод зберігається Рідкісний випадок: переїзд API, де важливий POST
410 Видалено назавжди Сторінки більше немає й заміни їй немає

Плутати 301 і 302 — найдорожча помилка в цьому списку: за 302 пошуковик лишає в індексі стару адресу й вагу на нову не передає.

Правила для .htaccess

Усі приклади розраховані на Apache з увімкненим mod_rewrite. Блок правил ставлять на початок файлу .htaccess у корені сайту — до правил CMS, інакше WordPress перехопить запит раніше.

RewriteEngine On
RewriteBase /

Одна сторінка на іншу

# простий варіант
Redirect 301 /old-page/ https://site.com/new-page/

# той самий результат через mod_rewrite, якщо решта правил теж на ньому
RewriteRule ^old-page/?$ https://site.com/new-page/ [R=301,L]

Склейка дзеркал: з www на без www

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

Зворотний випадок, коли головним дзеркалом обрано домен із www:

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

Перехід на HTTPS

RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

За Cloudflare чи іншим проксі умова %{HTTPS} не спрацює — до сервера запит доходить уже по HTTP. Там перевіряють заголовок:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

HTTPS і без www одним правилом

Головний прийом цього розділу: не ставити два правила поспіль, інакше вийде ланцюжок із двох переходів.

RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} ^www.(.+)$ [NC]
RewriteRule ^(.*)$ https://site.com/$1 [R=301,L]

Слеш у кінці адреси

# додати слеш
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !.[a-zA-Z0-9]{1,5}$
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.*)$ /$1/ [R=301,L]

# прибрати слеш
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [R=301,L]

Умови з !-f і !-d тут не формальність: без них правило почне переписувати адреси картинок та наявних папок.

Переїзд на новий домен

# зі збереженням адрес сторінок — так і треба робити
RewriteCond %{HTTP_HOST} ^(www.)?old-site.com$ [NC]
RewriteRule ^(.*)$ https://new-site.com/$1 [R=301,L]

# усе на головну — лише якщо структура нового сайту геть інша
RewriteRule ^(.*)$ https://new-site.com/ [R=301,L]

Другий варіант пошуковики часто вважають «м’якою 404» і вагу не передають. Переносьте адресу в адресу скрізь, де це можливо, а на головну відправляйте лише те, чому заміни немає.

Розділ на розділ і адреси з параметрами

# /blog/стаття/ → /news/стаття/
RewriteRule ^blog/(.*)$ /news/$1 [R=301,L]

# ?id=12 → /catalog/sofa/
RewriteCond %{QUERY_STRING} (^|&)id=12(&|$)
RewriteRule ^index.php$ /catalog/sofa/? [R=301,L]

Знак питання в кінці цілі відкидає вихідний рядок параметрів — без нього він приклеїться до нової адреси.

Те саме на nginx

У nginx немає .htaccess: правила живуть у конфігу сервера й застосовуються після nginx -t та перезавантаження.

server {
    listen 80;
    server_name www.site.com site.com;
    return 301 https://site.com$request_uri;
}

# одна сторінка на іншу
location = /old-page/ {
    return 301 https://site.com/new-page/;
}

# розділ на розділ
rewrite ^/blog/(.*)$ /news/$1 permanent;

Конструкція return 301 швидша за rewrite й читається однозначно, тому для простих випадків беруть саме її.

Часті помилки

ТАК БУВАЄ: ЧОТИРИ ПЕРЕХОДИ ЗАМІСТЬ ОДНОГОhttp://www.site.comhttp://site.comhttps://site.comhttps://site.com/page/Кожен крок — окремий запит: довше завантаження, частина ваги губиться, робот витрачає ліміт обходуЯК ТРЕБА: ОДНЕ ПРАВИЛО, ОДИН ПЕРЕХІДhttp://www.site.comодин 301https://site.com/page/Перевіряється однією командою: curl -sIL https://site.com/page/ — у відповіді має бути рівно один 301
Ланцюжок із кількох переходів і те, як має бути
  • Ланцюжок редиректів. www → без www → https → слеш: чотири запити замість одного. Збирайте умови в одне правило.
  • Цикл. Правило відправляє сторінку саму на себе, браузер показує ERR_TOO_MANY_REDIRECTS. Зазвичай причина — редирект на HTTPS за проксі через %{HTTPS}.
  • 302 замість 301. Переїзд зроблено, а вага лишилася на старій адресі.
  • Усе на головну. Масовий редирект сотень сторінок у корінь пошуковик вважає підміною 404.
  • Правила після правил CMS. У .htaccess WordPress перехопить запит першим, і ваше правило не спрацює: ставте свій блок вище.
  • Редирект замість canonical. Якщо обидві сторінки потрібні людям, а дублюється лише вміст, ставлять rel="canonical", а не 301.
  • Забуті внутрішні посилання. Після переїзду посилання всередині сайту мають вести на нову адресу напряму, інакше кожен перехід відвідувача йде через редирект.

Як перевірити

Найшвидше — командою в терміналі. Ключ -I запитує лише заголовки, -L іде ланцюжком до кінця:

curl -sIL https://site.com/old-page/ | grep -E "HTTP/|Location"

Правильна відповідь виглядає так: один HTTP/1.1 301, один Location і далі HTTP/2 200. Два й більше 301 поспіль — це ланцюжок, його треба схлопнути. У Search Console той самий результат видно у звіті «Індексування сторінок», а в браузері — у вкладці Network, якщо увімкнути «Preserve log».

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

Коротко про головне

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

Якщо переїзд великий — зміна домену, об’єднання сайтів, перехід на HTTPS із перебудовою адрес — напишіть нам: складемо карту відповідностей і перенесемо так, щоб трафік лишився на місці.