SEO для малого бизнеса

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

Готовые примеры редиректов для смены адресов, склейки зеркал и переезда на https, порядок правил и как не поймать цикл.

12 минут чтения 2 Дмитрий Акиндинов
Содержание
  1. Когда нужен 301, а когда лучше ничего не трогать
  2. Redirect или RewriteRule: что выбрать
  3. Готовые блоки правил
  4. Порядок правил решает всё
  5. Цикл редиректов: как поймать и разорвать
  6. Проверка после настройки
  7. Когда htaccess просто не работает
  8. Что делать дальше
  9. Частые вопросы

301 редирект в .htaccess пишется одной строкой: Redirect 301 со старого адреса на новый для точных совпадений, RewriteRule для шаблонов. Порядок правил важнее их содержания: срабатывает первое подходящее, и одно жадное правило вверху файла перекрывает всё, что стоит ниже.

Сайт переехал на новые адреса, старые ссылки отдают 404, позиции поплыли за две недели. Или обратная беда: кто-то нагородил правил, и браузер пишет «слишком много перенаправлений». 301 редирект через htaccess закрывает оба случая, если правила написаны в правильном порядке и без самодеятельности. Ниже готовые блоки, которые я копирую клиентам шестой год подряд, разбор очерёдности правил, диагностика цикла и проверка результата через curl. Плагины для этого не нужны ни на WordPress, ни на Битриксе.

Изогнутая стрелка перенаправления с одной страницы на другую

Сразу оговорюсь: .htaccess работает только на Apache и совместимых с ним серверах. Если у вас чистый nginx, все примеры ниже придётся переписать в конфиг сервера. Про это отдельный раздел в конце, не пропустите его, если правила упорно не срабатывают.

Когда нужен 301, а когда лучше ничего не трогать

301 говорит поисковику: страница переехала навсегда, склей старый адрес с новым и передай туда накопленный вес. Яндекс и Google это делают, но не мгновенно: склейка занимает от двух недель до пары месяцев, в зависимости от того, как часто робот заходит на сайт. Браузер кэширует 301 жёстко и надолго, иногда до очистки профиля. Поэтому ошибочное правило живёт у посетителя дольше, чем на сервере, и это главный аргумент не экспериментировать на живом сайте.

302 и 307 означают «временно». Вес не передаётся, в индексе остаётся старый адрес. Это нормальный выбор для заглушки на время работ или для сезонной акции, которую через месяц уберут.

КодЧто понимает роботКэш браузераТиповой случай
301переехало навсегдадолгийсмена URL, склейка зеркал, переход на https
302временно, старый адрес живойнетзаглушка на время работ, сезонная страница
307временно, метод запроса сохраняетсянетHSTS, редиректы для POST
308навсегда, метод сохраняетсядолгийAPI, на сайте услуг почти не нужен
410удалено окончательно, замены нетнетзакрытая услуга, снятая с продажи позиция

Есть третий вариант, о котором забывают: не делать редирект вообще. Если страница просто дублируется по двум адресам и обе версии нужны людям, правильнее поставить канонический адрес, а не резать один из URL насмерть. Разница и типовые ошибки разобраны в статье про канонические ссылки.

И ещё одна вещь, за которую я регулярно ругаюсь с подрядчиками. Не надо сваливать все удалённые страницы на главную. Робот видит массовый редирект нерелевантных URL на один адрес и обрабатывает их как обычные 404, только медленнее. Если замены странице нет, честнее отдать 404 или 410. Если замена есть, редирект должен вести на конкретный аналог, а не в корень сайта. Как искать такие хвосты, писал в разборе про битые ссылки и 404.

Redirect или RewriteRule: что выбрать

В Apache два разных модуля умеют перенаправлять, и путаница между ними стоит людям часов отладки.

Redirect и RedirectMatch живут в модуле mod_alias. Синтаксис короткий, работает по началу пути, регулярные выражения только у RedirectMatch. Этого хватает для точечных случаев.

# Одна страница на другую, точное совпадение по началу пути
Redirect 301 /uslugi/staraya-stranitsa /uslugi/novaya-stranitsa

# То же самое регуляркой, с якорем конца строки
RedirectMatch 301 ^/uslugi/staraya-stranitsa/?$ /uslugi/novaya-stranitsa/

Ловушка простого Redirect: он матчит префикс. Правило для /uslugi поймает и /uslugi-i-tseny, и /uslugi/podderzhka/, и хвост допишется к целевому адресу. Поэтому для чего-то сложнее одной статичной страницы я беру RedirectMatch с явным ^ и $ либо сразу mod_rewrite.

RewriteRule из mod_rewrite умеет условия: смотреть на домен, протокол, user-agent, наличие файла на диске. Всё, что зависит от контекста запроса, делается только им.

RewriteEngine On

# Условие + правило: редирект срабатывает, только если хост совпал
RewriteCond %{HTTP_HOST} ^old-domain.ru$ [NC]
RewriteRule ^(.*)$ https://new-domain.ru/$1 [R=301,L]

Практическое правило: если редирект зависит от чего-то кроме пути, это mod_rewrite. Если это одна фиксированная страница на другую, Redirect 301 короче и читается лучше. Смешивать оба модуля в одном файле можно, но помните, что mod_rewrite отрабатывает раньше mod_alias, и правило из RewriteRule перехватит запрос до того, как до него доберётся Redirect.

Флаги, которые нужны в 95 процентах случаев:

  • R=301 отдать код 301 (без числа будет 302),
  • L последнее правило, дальше не обрабатывать,
  • NC без учёта регистра,
  • QSA дописать исходную строку параметров к новому адресу,
  • QSD наоборот, выбросить параметры.

Готовые блоки правил

Дальше идут заготовки, которые я держу в сниппетах и правлю под клиента. Все они кладутся в .htaccess в корне сайта.

Страница на страницу

RewriteEngine On

RewriteRule ^kontakty-2$ /kontakty/ [R=301,L]
RewriteRule ^uslugi/podderzhka-saitov$ /uslugi/tehpodderzhka/ [R=301,L]

Обратите внимание: в файле .htaccess путь в шаблоне идёт без ведущего слеша, а в целевом адресе слеш нужен. Это первая причина, по которой скопированное из чужого конфига правило молчит.

Каталог целиком с сохранением структуры

# /catalog/uslugi/remont/ -> /uslugi/remont/
RewriteRule ^catalog/uslugi/(.*)$ /uslugi/$1 [R=301,L]

Скобки захватывают хвост, $1 подставляет его в новый адрес. Если хвост подставлять не надо и вся ветка схлопывается в одну страницу, скобки убираем, а в конец целевого адреса ставим ?, иначе Apache допишет параметры запроса:

RewriteRule ^arhiv/ /blog/? [R=301,L]

Смена домена

RewriteEngine On

RewriteCond %{HTTP_HOST} ^(www.)?old-domain.ru$ [NC]
RewriteRule ^(.*)$ https://new-domain.ru/$1 [R=301,L]

Такой блок переносит на новый домен всю карту адресов один в один. Если структура при переезде тоже менялась, сначала идут точечные правила на изменившиеся URL, и только потом общий блок. Полный порядок действий при смене домена, вместе с Вебмастером и sitemap, расписан в статье про переезд на новый домен.

С http на https

RewriteEngine On

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

Если сайт стоит за прокси или балансировщиком (типичная история на shared-хостинге с LiteSpeed или на CDN), переменная %{HTTPS} может быть пустой даже при живом сертификате. Тогда сайт уйдёт в бесконечный цикл. Рабочая универсальная версия проверяет ещё и заголовок от прокси:

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

www и без www

Выберите одну версию как основную и склейте на неё вторую. Какую именно, значения не имеет, важно только не менять решение раз в полгода.

# Склейка на версию без www
RewriteCond %{HTTP_HOST} ^www.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

%1 берётся из последнего RewriteCond, $1 из RewriteRule. Перепутать их легко, а результат будет выглядеть как редирект в никуда.

Хвосты, которые портят статистику

Четыре мелочи, которые дают дубли и размывают позиции. Каждая правится одной строкой.

# Всегда со слешем в конце, кроме реальных файлов
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(/$|.)
RewriteRule ^(.*)$ /$1/ [R=301,L]

# Убрать index.php из адреса
RewriteCond %{THE_REQUEST} s/+index.php[s?] [NC]
RewriteRule ^ /? [R=301,L]

# Привести адрес к нижнему регистру (только для конкретной ветки)
RewriteMap lc int:tolower

Последний пример работает не везде: RewriteMap объявляется в конфиге сервера, а в .htaccess его использовать нельзя. На shared-хостинге проблему с регистром проще решить точечными правилами на конкретные адреса.

Порядок правил решает всё

Apache читает .htaccess сверху вниз и на первом совпавшем правиле с флагом L останавливается. Отсюда два следствия, из-за которых обычно всё и ломается.

Первое: общие правила ставим ниже частных. Если блок «весь старый домен на новый» стоит первым, точечные правила под ним никогда не сработают. Робот получит редирект на новый домен по старому пути и там 404.

Второе: правила самой CMS должны идти последними. У WordPress это блок между комментариями # BEGIN WordPress и # END WordPress, у Битрикса свой набор в корневом .htaccess. Свои редиректы кладём выше этого блока, иначе фронт-контроллер CMS перехватит запрос раньше и отдаст 404 своими силами.

Рабочая структура файла, которую я держу у всех клиентов:

# 1. Протокол и зеркала
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

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

# 2. Точечные редиректы страниц
RewriteRule ^kontakty-2$ /kontakty/ [R=301,L]

# 3. Редиректы разделов
RewriteRule ^catalog/uslugi/(.*)$ /uslugi/$1 [R=301,L]

# BEGIN WordPress
# ... штатный блок CMS, не трогаем ...
# END WordPress

Ещё одна деталь про порядок: кэширующие директивы и правила блокировки ботов живут в этом же файле и тоже конфликтуют по очерёдности. Если у вас там уже настроены заголовки кэша или запреты по user-agent, добавляйте редиректы над ними и проверяйте, что ничего не поехало.

Цикл редиректов: как поймать и разорвать

Симптом узнаваемый: браузер пишет «ERR_TOO_MANY_REDIRECTS» или «слишком много перенаправлений», сайт не открывается вообще. Причин у цикла всего несколько, и все они лечатся за десять минут, если знать, где смотреть.

Правило редиректит само на себя. Классика с трейлинг-слешем: правило добавляет слеш, а CMS его снимает. Или наоборот. Лечится добавлением условия, которое исключает уже правильные адреса.

Сервер не видит https. Разобрано выше: %{HTTPS} пустая за прокси, правило гонит на https бесконечно. Проверяется добавлением условия на X-Forwarded-Proto.

Редирект настроен в двух местах сразу. У WordPress это любимая история: правило есть в .htaccess, плагин редиректов делает то же самое, а поверх ещё стоит канонический редирект ядра. Правильно оставить один источник правды. Мой выбор всегда .htaccess, потому что он отрабатывает до загрузки PHP и не зависит от того, жив ли сайт.

Разъехались настройки CMS и сервера. В WordPress адреса siteurl и home записаны в базе. Если там http://www.site.ru, а .htaccess гонит на https://site.ru, получаем качели. Проверить и поправить:

wp option get siteurl
wp option get home
wp option update siteurl 'https://site.ru'
wp option update home 'https://site.ru'

Быстрая диагностика без гадания: смотрим всю цепочку целиком, а не конечный результат.

curl -sIL -A "Mozilla/5.0" http://www.site.ru/staraya-stranitsa 
  | grep -Ei "^(HTTP/|location:)"

Вывод покажет каждый шаг: сколько было переходов, какие коды, куда вёл каждый. Если в ответе повторяются два адреса по кругу, вы нашли пару правил, которые дерутся. Здоровая цепочка после переезда это один или два перехода, не больше. Три и выше уже стоит распутывать: каждый лишний хоп это потерянное время загрузки и риск, что робот не дойдёт до конца.

Если сайт лёг совсем и админка недоступна, аварийный ход простой: переименовать .htaccess в .htaccess.bak через файловый менеджер хостинга, убедиться, что сайт поднялся, и разбирать правила уже на живом сайте. У WordPress штатный блок восстанавливается сам при сохранении постоянных ссылок в админке.

Проверка после настройки

Онлайн-чекеры удобны для одиночной ссылки, но для переезда на сотню адресов нужен список. Я делаю так: выгружаю все старые URL (из sitemap, из отчёта Вебмастера, из выгрузки старой CMS), кладу в файл и прогоняю пачкой.

while read url; do
  code=$(curl -s -o /dev/null -w "%{http_code}" -I "$url")
  target=$(curl -s -o /dev/null -w "%{redirect_url}" -I "$url")
  echo "$code  $url  ->  $target"
done < old-urls.txt

На выходе таблица, в которой глазами видно все 404 и все редиректы в неожиданные места. Пять минут работы вместо часа кликов по чекерам.

Чек-лист проверки после включения правил:

  • главная открывается по всем четырём вариантам адреса (http, https, с www, без www) и все три лишних варианта дают ровно один 301 на основной;
  • десяток случайных внутренних страниц отдают 200, а не цепочку из трёх переходов;
  • картинки, CSS и JS грузятся по https без смешанного контента;
  • формы отправляются и заявка доходит (редирект с POST на другой домен теряет данные, если стоит 301 вместо 307);
  • админка открывается и сохраняет изменения;
  • в Яндекс Вебмастере через несколько дней в разделе с ошибками не растёт число недоступных страниц;
  • sitemap.xml содержит только конечные адреса, без старых URL, которые сами редиректят;
  • внутренние ссылки в контенте ведут на новые адреса напрямую, а не через редирект.

Последний пункт пропускают почти все. Редирект нужен для внешних ссылок и для роботов, а внутри сайта ссылки надо переписать в базе. Иначе каждый клик посетителя делает лишний переход, а вы годами таскаете технический долг.

Когда htaccess просто не работает

Бывает, что правило написано верно, а эффекта ноль. Проверьте по порядку.

Файл не читается сервером. На nginx без Apache .htaccess игнорируется полностью, никаких ошибок при этом не будет. Проверить, что за сервер у вас, можно одной командой:

curl -sI https://site.ru | grep -i "^server:"

Если в ответе nginx без упоминания Apache, правила надо переносить в конфиг сервера, а на shared-хостинге это делается только через поддержку. Чем отличаются связки Apache, nginx и LiteSpeed и что из этого стоит просить у хостера, разбирал в статье про выбор веб-сервера.

Запрещено переопределение. Директива AllowOverride None в конфиге сервера отключает .htaccess для директории. На нормальном shared-хостинге такого не встречал, на VPS после ручной настройки Apache встречал регулярно.

Модуль не подключен. Без mod_rewrite все блоки с RewriteRule либо молчат, либо роняют сайт в 500. Оберните правила в проверку, чтобы не положить сайт целиком:

<IfModule mod_rewrite.c>
  RewriteEngine On
  # правила
</IfModule>

Кэш браузера. Вы уже поймали старый 301, и браузер честно ведёт вас по нему, не спрашивая сервер. Проверять правила надо в приватном окне или через curl, а не в основном профиле. Я на этом однажды потерял сорок минут, доказывая клиенту, что редирект работает.

Права на файл. Если .htaccess лежит с правами 600 и владельцем root, веб-сервер его не прочитает. Нормально это 644 для файла и 755 для директории.

Что делать дальше

Порядок работ при переезде адресов у меня выглядит так. Сначала собираю полный список старых URL и составляю карту соответствия: каждому старому адресу конкретный новый, а не главная. Потом пишу правила локально или на тестовой копии и прогоняю curl по всему списку. Только после этого правила уезжают на боевой сайт, и там же сразу проверяются заново. В конце переписываю внутренние ссылки в базе и обновляю sitemap.

Разовая настройка редиректов для сайта на 30-50 страниц занимает у меня 2-4 часа вместе с картой адресов и проверкой. Если переезд большой и адресов несколько сотен, время уходит в основном на карту соответствия, а не на сам .htaccess. У клиентов на абонентском обслуживании такие работы обычно попадают в месячный лимит часов, отдельной сметы не требуют. Условия и что входит в тарифы, смотрите в разделе с ценами: годовой пакет 35 000 рублей, помесячный от 15 000.

Если сайт уже лежит с циклом редиректов или после переезда посыпались 404 и позиции, это тот случай, когда каждый день ожидания стоит трафика. Напишите через форму на главной, опишите, что менялось и когда, и приложите вывод curl -sIL по проблемному адресу. С этими данными диагностика занимает минуты, а не переписку на три дня.

Частые вопросы

Как сделать 301 редирект через htaccess

Для одиночного адреса хватает директивы Redirect 301 со старым и новым путём. Для шаблона нужен RewriteRule с флагами R=301 и L. Файл кладут в корень сайта, рядом с index.php. Проверять надо ответ сервера, а не глазами в браузере: браузер кэширует редиректы надолго.

Чем Redirect отличается от RewriteRule

Redirect проще и подходит, когда старый адрес переезжает на новый один в один. RewriteRule нужен там, где есть шаблон: перенос раздела целиком, склейка параметров, условия по домену или протоколу.

Почему браузер пишет про слишком много перенаправлений

Правила зациклились: одно ведёт на второе, второе обратно на первое. Чаще всего виноват редирект на самого себя, который срабатывает уже после переписывания адреса. Ловится по цепочке ответов сервера, а не по коду.

Почему htaccess не работает

Либо сервер не Apache: на чистом nginx файл просто игнорируется, правила переносят в конфиг сервера. Либо для директории запрещено переопределение настроек, и тогда хостинг должен разрешить его в конфигурации.

Передайте техническую часть в надёжные руки.

Слежу за хостингом, бэкапами и обновлениями. Когда что-то пойдёт не так – звоните мне, не своему программисту, не в спортзал и не маме.

Короткие вопросы по теме

Все вопросы по теме "SEO для малого бизнеса"

Заявка

Обсудим задачу

Напишите имя и телефон, выберите удобный канал. Перезвоню или напишу в течение часа в рабочее время.

Куда удобнее ответить?