Маршрутизация в Битрикс: urlrewrite.php, SEF или роутер D7
Каталог на Битриксе отдаёт 404 после деплоя, хотя в админке всё настроено. Сравниваем три слоя маршрутизации: urlrewrite.php, SEF-режим компонентов и роутер D7, и разбираем, где какой ломается.

Страница раздела каталога открывается, а адрес вида /catalog/shoes/nike-air/ отдаёт 404. В админке всё настроено, на тестовом сайте всё работало. Частая причина: правило ЧПУ существовало только в urlrewrite.php на проде, а выкладка из git затёрла файл. В Битриксе три способа описать маршрут, и каждый ломается по-своему. Ниже сравниваем правила в urlrewrite.php, SEF-режим комплексных компонентов и роутер D7. Материал написан для «1С-Битрикс: Управление сайтом» с актуальным модулем main. Есть ли роутер D7 в вашей версии и как он подключается, сверьте с документацией.
Как запрос доходит до страницы
Веб-сервер сначала ищет файл или каталог по адресу. Не нашёл, передаёт запрос в /bitrix/urlrewrite.php. Тот сверяет адрес с правилами из файла urlrewrite.php в корне сайта и подключает нужный PHP-файл. Если правило не нашлось, вы увидите 404. Для nginx в стандартном окружении этот переход выглядит так (в BitrixEnv и самописных сборках конфиг может отличаться):
location / {
try_files $uri $uri/ /bitrix/urlrewrite.php$is_args$args;
}Роутер D7 работает через собственную точку входа. Её подключают правила веб-сервера из комплекта поставки, поэтому на старых конфигах nginx и .htaccess их, возможно, придётся дополнить. Точный вид правил смотрите в документации вашей версии.
ЧПУ — человекопонятный URL: /catalog/shoes/ вместо /catalog/index.php?SECTION_ID=12. В Битриксе так называют и сам адрес, и режим SEF в компонентах.
Подход 1. Правила в urlrewrite.php
Это нижний слой. Каждое правило связывает регулярное выражение с PHP-файлом и при желании передаёт в него переменные. Структура записи рабочая, но обычно файл генерирует админка, а руками его правят только в особых случаях:
<?php
// /urlrewrite.php в корне сайта
$arUrlRewrite = array(
// Специфичное правило стоит раньше общего
array(
'CONDITION' => '#^/news/([a-z0-9-]+)/#',
'RULE' => 'ELEMENT_CODE=$1',
'ID' => '',
'PATH' => '/news/detail.php',
'SORT' => 100,
),
// Правило комплексного компонента каталога
array(
'CONDITION' => '#^/catalog/#',
'RULE' => '',
'ID' => 'bitrix:catalog',
'PATH' => '/catalog/index.php',
'SORT' => 100,
),
);Выигрываете вы универсальность: так можно отправить любой адрес в любой файл, не трогая компоненты. Платите тем, что маршрут размазан между регулярным выражением, файлом и переменными в $_GET. Читать такую карту сайта сложно, а порядок правил важен: срабатывает первое подходящее.
Подход 2. SEF-режим комплексного компонента
Если адрес целиком обслуживает один комплексный компонент (каталог, новости), маршруты описываются в его параметрах. Битрикс сам регистрирует правило в urlrewrite.php, а компонент разбирает остаток пути по шаблонам.
<?php
// /catalog/index.php, фрагмент вызова компонента bitrix:catalog
$APPLICATION->IncludeComponent(
'bitrix:catalog',
'',
array(
'SEF_MODE' => 'Y',
'SEF_FOLDER' => '/catalog/',
'SEF_URL_TEMPLATES' => array(
'sections' => '',
'section' => '#SECTION_CODE_PATH#/',
'element' => '#SECTION_CODE_PATH#/#ELEMENT_CODE#/',
),
// Компонент сам отдаёт код 404, если раздел не найден
'SET_STATUS_404' => 'Y',
// IBLOCK_TYPE, IBLOCK_ID и прочие обязательные параметры задайте под свой проект
),
false
);Набор ключей в SEF_URL_TEMPLATES зависит от компонента: у каталога они одни, у новостей другие. Список смотрите в документации конкретного компонента.
Выигрываете понятную схему: адреса лежат рядом с вызовом компонента и версионируются вместе со страницей. Платите привязкой к компоненту. Нестандартный адрес внутри каталога, например /catalog/sale/, в шаблоны не вписывается, и приходится добавлять отдельное правило выше в urlrewrite.php.
Подход 3. Роутер D7
Новый слой работает как в современных PHP-фреймворках. Маршрут описывается в файле, привязан к HTTP-методу и может содержать параметры в пути. Схематично, имена методов проверьте в документации вашей версии main:
<?php
// Схематично: файл маршрутов, подключённый через настройку routing
use Bitrix\Main\Routing\RoutingConfigurator;
return function (RoutingConfigurator $routes) {
$routes->get('/api/ping/', function () {
return 'ok';
});
};Где лежит файл маршрутов и как он регистрируется в .settings.php, зависит от версии. Откройте раздел документации о роутинге для вашего релиза. Не копируйте пути из чужих проектов.
Выигрываете разделение по методам GET и POST, именованные маршруты и привычную для фреймворков структуру. Платите тем, что публичные страницы с компонентами по-прежнему держатся на urlrewrite.php. Роутер не заменяет SEF каталога, он закрывает новые эндпоинты и API. Ajax-контроллеры D7 — отдельная тема, здесь их не разбираем.
Сравнение
| Подход | Где хранится маршрут | Что выигрываете | Чем платите |
|---|---|---|---|
| urlrewrite.php | Файл в корне сайта | Любой адрес в любой файл | Порядок правил, переменные через $_GET, файл часто генерируется на проде |
| SEF компонента | Параметры вызова компонента | Адреса рядом с кодом страницы | Только схема компонента, нестандартные адреса не вписать |
| Роутер D7 | Файл маршрутов | Методы HTTP, параметры в пути, имена маршрутов | Зависимость от версии и конфигов веб-сервера, не заменяет SEF каталога |
Один адрес должен жить в одном слое. Если то же ЧПУ описано и в urlrewrite.php, и в роутере, вы получите ошибку, которую заметят только на проде.
Когда что выбирать
- Адрес целиком обслуживает комплексный компонент (каталог, новости, блог): SEF-режим. Критерий: внутри раздела логику разбора пути ведёт сам компонент.
- Один нестандартный адрес должен открывать конкретный файл: правило в urlrewrite.php. Критерий: у адреса нет логики, кроме выбора файла и пары переменных.
- Нужен эндпоинт с разным поведением для GET и POST или ссылки по имени маршрута: роутер D7. Критерий: вы пишете код, а не собираете страницу из компонентов.
Что ломается
- Правило есть только на проде. После деплоя из git адрес отдаёт 404. Держите urlrewrite.php в репозитории и правьте его там, а не только через админку.
- Общее правило перекрывает частное. Условие вроде #^/catalog/# стоит выше #^/catalog/sale/#, и второе никогда не срабатывает.
- Совпадение с реальным файлом. Если по адресу лежит каталог с index.php, веб-сервер отдаст его, не дойдя до правил.
- Код 200 вместо 404. Без SET_STATUS_404 компонент может показать пустую страницу с кодом 200, и поисковик индексирует мусорные адреса.
- Роутер D7 молчит. Запрос не доходит до его точки входа из-за старого конфига nginx или .htaccess.
- Адрес описан в двух слоях сразу. Какой сработает первым, проверьте на своей версии. Лучше не оставлять такой неоднозначности.
Что делать дальше
Проверьте работающий сайт: запросите несуществующий адрес внутри каталога через curl -I и убедитесь, что получаете 404, а не 200. Затем сравните urlrewrite.php на проде с тем, что лежит в репозитории. Если расходятся, договоритесь, где источник правды. В логах nginx следите за долей 404 на разделах с SEF: резкий рост после выкладки означает, что правила потерялись.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Документация для разработчиков 1С-Битрикс: разделы о ЧПУ и роутинге
Частые вопросы
Почему после деплоя ЧПУ каталога дают 404?
Как понять, что роутер D7 есть в моей версии?
Что делать, если правило добавлено, а адрес всё равно 404?
Можно ли смешивать SEF компонента и роутер D7?
На что смотреть в проде при работе с ЧПУ?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.