Назад в блог

Redirects умерли в день деплоя. Bitrix об этом не предупреждает.

Мы переехали на headless в феврале. В апреле SEO-специалист прислал выгрузку: 340 битых ссылок. Три года работавших редиректов. Ноль ошибок в логах — потому что Next.js не знал, что они вообще должны были существовать.

Bitrix хранит правила редиректов внутри PHP. Когда фронт уходит на отдельный процесс, все эти правила остаются там — и работать перестают молча.

Как Bitrix управляет редиректами

Стандартный Bitrix использует два механизма.

Первый — URLREWRITE.PHP в корне сайта. Это PHP-файл с массивом правил: [CONDITION][PATH], [QUERY]. Он подключается к каждому запросу через bitrix/.settings.php. Когда Bitrix отдавал HTML сам — всё работало. Когда страницу рендерит Next.js — файл URLREWRITE.PHP не вызывается вообще.

Второй — таблица b_urlrewrite в базе данных. Туда пишет модуль SEO и SEO-фильтр каталога. Редиректы для «старых URL» товаров, изменённых URL категорий, ручные правила из админки. Та же история: Next.js её не читает.

В итоге 340 правил, которые копились три года, в день деплоя просто исчезли из жизни пользователя и GoogleBot.

Три варианта решения

Мы рассматривали три подхода. У каждого своя цена.

Первый — всё тянуть в middleware.ts. Правила читаются на Edge перед рендером, 301 отдаётся без roundtrip к серверу. Звучит чисто. Ломается через две недели, когда контент-менеджер добавил редирект в Bitrix-админке и думает, что он заработал.

Второй — Nginx map-файл. Nginx читает плоский файл с правилами и делает return 301 до Next.js. Быстро. Но файл надо регенерировать при каждом изменении: скрипт ходит в b_urlrewrite, строит map, делает nginx -s reload. Это CI/CD pipeline для редиректов. Не шутка.

Третий — гибрид плюс catch-all на 404. Nginx берёт статические правила из b_urlrewrite (те, что меняются раз в квартал). Next.js middleware — динамические: SEO-фильтр, параметрические URL. Для всего остального — app/[...not-found]/page.tsx, который при 404 спрашивает Bitrix: «есть редирект для этого URL?»

REST API Bitrix таблицу редиректов не отдаёт нативно. Нужен кастомный handler на PHP. Мы написали метод в local/php_interface/iblockHandlers.php, который принимает URL и возвращает правило из b_urlrewrite или null.

Latency на catch-all — реальная проблема. Каждый 404 делает запрос на бэкенд. При 50k сессий/день это заметно в p95. Решение: Redis с TTL 24 часа. Настоящий 404 — быстро. Найденный редирект — быстро. Подтверждённый «редиректа нет» — тоже кешируется отдельным ключом.

Что мы выбрали

Гибрид. Nginx обрабатывает 280 из 340 правил — те, что статичны: старые URL товаров после ребрендинга, переименованные категории. Они меняются раз в квартал, скрипт регенерации запускается через cron.

Оставшиеся 60 — динамические правила SEO-фильтра и параметрические URL — Next.js middleware. Middleware читает JSON-файл, который генерируется той же задачей cron.

Catch-all на 404 — для случаев, которые мы пропустили. За первые три месяца он поймал 22 редиректа, которые не попали в первую выгрузку.

Операционный вопрос

Это тот момент, который обычно упускают при планировании headless-миграции: кто теперь управляет правилами редиректов?

Раньше контент-менеджер шёл в Bitrix-админку, добавлял правило, нажимал «Сохранить» — и оно работало. Теперь это работает только если правило попадает в базу, оттуда в скрипт, потом в Nginx map или в middleware.json, потом на сервер.

Мы решили так: контент-менеджер работает как раньше — в Bitrix-админке. Скрипт синхронизации запускается по cron раз в 6 часов. Есть кнопка «синхронизировать сейчас» в личном инструменте — для срочных случаев. Задержка 6 часов никого не устроила поначалу, но после объяснения (альтернатива — CI/CD на каждое изменение) договорились.

Что мы не учли

Три вещи стоили нам времени.

Первая: URLREWRITE.PHP и таблица b_urlrewrite — не одно и то же. У нас было 40 правил только в файле, которые никогда не попадали в базу. Они исчезли первыми и нашлись последними.

Вторая: SEO-фильтр Bitrix создаёт канонические URL и редиректы динамически при изменении параметров фильтрации. Эти правила нельзя выгрузить статически — они генерируются в момент запроса. Nginx map для них не подходит. Их нужно перехватывать на уровне Next.js middleware с логикой разбора URL.

Третья: при тестировании сценарий «пользователь переходит по старому URL» надо проверять через GoogleBot User-Agent, а не только в браузере. Некоторые правила в Bitrix работали только для определённых User-Agent'ов — и в браузере казалось, что всё в порядке.

Итог

Headless не ломает редиректы. Он просто перестаёт их выполнять, потому что они жили там, куда Next.js не смотрит.

Аудит редиректов — первое, что нужно делать до перехода на headless, а не через два месяца после. Выгружаете b_urlrewrite, смотрите на URLREWRITE.PHP, считаете количество правил. Если их больше ста — закладывайте неделю на архитектуру синхронизации.

340 битых ссылок — это три года SEO-работы. Они вернулись за 4 дня. Но только потому, что знали, где искать.


*Если переходите на headless Bitrix + Next.js — смотрите на 5 сюрпризов Bitrix REST API и что мы оставили на Bitrix при переходе на headless. Редиректы — не единственное, что живёт в PHP и уходит тихо.*