У вас переезжает блог с тысячей статей. Старые URL вида /2021/08/15/nazvanie-stati должны стать /blog/nazvanie-stati. Создавать тысячу правил вручную — очевидно не вариант. Но и невозможно предусмотреть каждую статью заранее.
Regex-редиректы решают именно это: одним правилом описывается шаблон, под который подпадают тысячи URL — и каждый перенаправляется корректно, сохраняя slug статьи.
Зачем нужны regex-редиректы
Обычный редирект — это отношение один-к-одному: конкретный URL источник → конкретный URL назначения. Это идеально для точечных случаев.
Regex-редирект — это отношение многие-к-одному-шаблону: любой URL, соответствующий паттерну, перенаправляется по правилу с подстановкой захваченных частей.
Типичные задачи, где без regex не обойтись:
- Смена CMS — WordPress, Bitrix, 1С-Bitrix, Joomla, каждый со своей структурой URL. При переезде нужно перемапить тысячи страниц.
- Удаление .html — сайт перешёл на ЧПУ, все старые
страница.htmlдолжны вести настраница. - Реструктуризация каталога —
/catalog/category/subcategory/item→/products/item. - Нормализация trailing slash — единообразно убрать или добавить слеш в конце.
- Перенос блога — даты в URL стали ненужными, оставить только slug.
Синтаксис: минимум для работы
Для URL-редиректов достаточно знать небольшой набор конструкций:
| Символ | Значение | Пример |
|---|---|---|
^ | Начало строки | ^/blog — URL начинается с /blog |
$ | Конец строки | /blog$ — URL заканчивается на /blog |
. | Любой символ | a.b — «a», любой символ, «b» |
* | Ноль или больше предыдущего | .* — любая строка любой длины |
+ | Один или больше предыдущего | .+ — любая непустая строка |
? | Ноль или один предыдущий | /blog/? — /blog или /blog/ |
\d | Цифра (0–9) | \d{4} — ровно 4 цифры |
[^/] | Любой символ кроме / | [^/]+ — один сегмент пути |
(...) | Захватывающая группа | ([^/]+) — захватить сегмент |
\ | Экранирование | \.html — буквальная точка |
Два правила, которые нарушают почти все при первом знакомстве с regex в URL:
Точка — это не точка. В regex . означает любой символ. Чтобы сопоставить буквальную точку (в расширении .html), нужно экранировать: \.html.
Слеш — это просто слеш. Он не имеет специального значения в regex (в отличие от некоторых других контекстов), поэтому экранировать его не нужно — хотя ошибки от лишних \/ обычно не возникает.
Захватывающие группы
Это ключевой механизм regex-редиректов. Часть паттерна, заключённая в скобки (...), запоминает совпавший текст. В URL назначения его можно вставить через $1, $2 и т.д. — по порядку открывающих скобок.
Пример с блогом:
Шаблон источника: ^/(\d{4})/(\d{2})/(\d{2})/([^/]+)$
URL назначения: /blog/$4
Для URL /2021/08/15/nazvanie-stati:
$1=2021(год)$2=08(месяц)$3=15(день)$4=nazvanie-stati(slug)
В итоге URL назначения: /blog/nazvanie-stati — год, месяц и день отброшены, slug сохранён.
Можно и объединять захваченное. Например, если нужен адрес /archive/2021/nazvanie-stati:
URL назначения: /archive/$1/$4
Захватывайте только то, что нужно в адресе назначения. Ненужные сегменты можно просто не заключать в скобки — они будут сопоставлены, но не захвачены.
7 готовых шаблонов
1. Удаление дат из URL блога
Источник: ^/\d{4}/\d{2}/\d{2}/([^/]+)/?$
Назначение: /blog/$1
Работает для /2021/08/15/statya, /2019/01/01/drugaya-statya и т.д.
2. Удаление расширения .html
Источник: ^/(.+)\.html$
Назначение: /$1
/about.html → /about, /catalog/item.html → /catalog/item. Точка экранирована — иначе совпало бы с /about_html.
3. Перенос раздела каталога
Источник: ^/catalog/old-category/([^/]+)/?$
Назначение: /products/new-category/$1
Все URL из старой категории → новая. Slug товара сохраняется.
4. Нормализация trailing slash (добавить)
Источник: ^(/[^.]*[^/])$
Назначение: $1/
Добавляет слеш к URL без расширения файла. /about → /about/, но /style.css не трогает.
5. Перенос вложенного пути целиком
Источник: ^/old-section/(.*)$
Назначение: /new-section/$1
/old-section/page → /new-section/page, /old-section/sub/page → /new-section/sub/page. (.*) захватывает весь остаток пути включая вложенность.
6. Извлечение параметра из пути
Источник: ^/products/id-(\d+)/?$
Назначение: /catalog/item?id=$1
/products/id-12345 → /catalog/item?id=12345. Числовой ID извлекается из slug и передаётся как query-параметр.
7. Двойной захват: перестановка сегментов
Источник: ^/([^/]+)/([^/]+)/?$
Назначение: /$2/$1
Меняет местами два сегмента пути. /category/subcategory → /subcategory/category. Используйте осторожно — паттерн очень широкий, захватит больше чем кажется.
Типичные ошибки
Незаякоренный шаблон. Без ^ в начале паттерн /blog/ сопоставится с любым URL, который содержит /blog/ в любом месте — включая /archive/blog/post. Всегда начинайте с ^.
Жадный .* там, где нужен [^/]+. Паттерн ^/([^/]+)/(.*)$ для /a/b/c захватит a в $1 и b/c в $2. А ^/(.*)/(.*)$ — непредсказуемо, потому что жадный .* попытается захватить максимум, и второй (.*) может оказаться пустым. Для сегментов пути используйте [^/]+.
Точка без экранирования. \.html и .html — разные вещи. Без экранирования /aboutXhtml тоже совпадёт.
Слишком широкий шаблон выше специфичного. Если ^/(.*)$ стоит раньше ^/blog/(.*)$ — второе правило никогда не сработает. Специфичные правила должны быть выше.
Бесконечный цикл. Шаблон ^/(.*)$ → /$1 никуда не редиректит — URL назначения совпадает с источником. Некоторые системы это поймают, другие — войдут в цикл.
Как тестировать
Перед применением на продакшене обязательно проверьте каждое правило против набора реальных URL.
regex101.com — стандартный инструмент. Вставляете шаблон, пишете тестовые строки, видите совпадения и захваченные группы в реальном времени. Выбирайте режим PCRE2.
Минимальный набор тестовых случаев для каждого правила:
- Типичный URL, который должен совпасть
- URL с trailing slash и без
- URL со спецсимволами и кириллицей
- URL, который не должен совпасть — убедитесь, что не совпадает
- Самый длинный и самый короткий возможный URL
После тестирования regex — проверьте финальный редирект через тестер редиректов. Иногда шаблон работает корректно, но URL назначения собирается неправильно из-за опечатки в $1/$2.
Хорошее правило: развёртывайте regex-правила по одному и мониторьте 404 в первые часы. Один неверный шаблон может накрыть большой раздел сайта.