Разработка сайтов и интернет-магазинов на 1С-битрикс, порталы Битрикс24, seo продвижение, реклама, поддержка проектов
Добавление тегов rel = Prev/Next и rel = canonical на страницы с пагинацией 1C-Битрикс

Добавление тегов rel = Prev/Next и rel = canonical на страницы с пагинацией 1C-Битрикс

Логотип Liberty
Константин Либерман
Константин Либерман
Основатель компании «LIBERTY»
26 просмотров

Для чего нужны эти теги в SEO?


Когда на сайте есть каталог товаров или блог с постраничной навигацией, поисковые роботы видят множество похожих URL: /catalog/, /catalog/?PAGEN_1=2, /catalog/?PAGEN_1=3 и так далее. Без правильных мета-тегов Google и Яндекс могут расценить эти страницы как дубли и размыть ссылочный вес — что плохо скажется на позициях сайта.

Теги rel="prev" и rel="next" сообщают поисковику: «Эти страницы — часть единой последовательности», помогая объединить их в один логический блок и корректно оценить общий вес. Тег rel="canonical" на страницах пагинации указывает основную версию URL (как правило, первую страницу или текущую без мусорных GET-параметров), исключая появление дублей в индексе.

В итоге три простых тега решают сразу две задачи: правильная кластеризация страниц пагинации и защита от дублей. Штатного инструмента для этого в 1С-Битрикс нет — реализуем сами.

У нас реализация была на компоненте news.list.

1. В нужном компоненте news.list создаем файл result_modifier.php. И вставляем следующий код, который только сохраняет данные пагинации:
result_modifier.php
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

$arResult['NAV_NUM']        = $arResult['NAV_RESULT']->NavNum;
$arResult['NAV_PAGE_NOMER'] = $arResult['NAV_RESULT']->NavPageNomer;
$arResult['NAV_PAGE_COUNT'] = $arResult['NAV_RESULT']->NavPageCount;
$arResult['SECTION_CODE']   = $arParams['SECTION_CODE'];

$this->__component->SetResultCacheKeys([
    'NAV_NUM',
    'NAV_PAGE_NOMER',
    'NAV_PAGE_COUNT',
]);

2. В этом же месте создаем еще один файл component_epilog.php. И вставляем следующий код -  сохраняем данные в глобальную переменную.
component_epilog.php
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

// Передаём данные пагинации в глобальную переменную для init.php
$GLOBALS['NEWS_PAGINATION'] = [
    'cur'  => (int)$arResult['NAV_PAGE_NOMER'],
    'last' => (int)$arResult['NAV_PAGE_COUNT'],
];

3. Вставляем в наш init.php следующий код - читаем глобальную переменную и патчим через буфер
init.php
// добавление prev next при постраничной навигации
AddEventHandler('main', 'OnEndBufferContent', 'updatePaginationPrevNext');

function updatePaginationPrevNext(&$content)
{
    if (!is_string($content)) return;

    $baseUrl = $_SERVER['REQUEST_URI'];

    // Чистим служебные параметры
    $baseUrl = preg_replace('/([?&])PAGEN_\\d+=\\d+(&|$)/i', '$1', $baseUrl);
    $baseUrl = preg_replace('/([?&])clear_cache=[^&]*(&|$)/i', '$1', $baseUrl);
    $baseUrl = preg_replace('/([?&])sessid=[^&]*(&|$)/i', '$1', $baseUrl);

    $baseUrl = rtrim($baseUrl, '?&');

    $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
    $host   = $scheme . '://' . strtok($_SERVER['HTTP_HOST'], ':');
    $glue   = strpos($baseUrl, '?') !== false ? '&' : '?';

    $inject = '';

    // prev / next из данных компонента
    if (!empty($GLOBALS['NEWS_PAGINATION'])) {
        $curPage  = $GLOBALS['NEWS_PAGINATION']['cur'];
        $lastPage = $GLOBALS['NEWS_PAGINATION']['last'];

        if ($curPage < $lastPage) {
            $inject .= '<link rel="next" href="' . $host . $baseUrl . $glue . 'PAGEN_1=' . ($curPage + 1) . '">' . "\n";
        }

        if ($curPage > 1) {
            $urlPrev = ($curPage - 1) > 1
                ? $host . $baseUrl . $glue . 'PAGEN_1=' . ($curPage - 1)
                : $host . $baseUrl;
            $inject .= '<link rel="prev" href="' . $urlPrev . '">' . "\n";
        }
    }

    // Удаляем старый canonical
    $content = preg_replace('#<link[^>]+rel=["\']canonical["\'][^>]*>\s*#is', '', $content);

    // Вставляем перед </head>
    $content = str_replace('</head>', $inject . '</head>', $content);

    // Title / Description / H1 / Keywords с номером страницы
    $pageNum = 0;
    foreach ($_GET as $key => $val) {
        if (preg_match('/^PAGEN_\\d+$/i', $key)) {
            $pageNum = (int)$val;
            break;
        }
    }

    if ($pageNum > 1) {
        $suffix = ' — страница ' . $pageNum;

        // Title
        $content = preg_replace(
            '#(<title>)(.*?)(</title>)#is',
            '$1$2' . htmlspecialchars($suffix) . '$3',
            $content
        );

        // Description
        $content = preg_replace(
            '#(name=["\']description["\'][^>]*content=["\'])([^"\']*?)(["\'])#is',
            '$1$2' . htmlspecialchars($suffix) . '$3',
            $content
        );

        // Keywords
        $content = preg_replace(
            '#(name=["\']keywords["\'][^>]*content=["\'])([^"\']*?)(["\'])#is',
            '$1$2' . htmlspecialchars($suffix) . '$3',
            $content
        );

        // H1 — только первый на странице
        $content = preg_replace(
            '#(<h1[^>]*>)(.*?)(</h1>)#is',
            '$1$2' . htmlspecialchars($suffix) . '$3',
            $content,
            1
        );
    }
}
// добавление prev next при постраничной навигации

Что получилось в итоге

После подключения кода в шаблоне компонента news.list каждая страница пагинации получила корректный набор мета-тегов в блоке . Проверим на примере четвёртой страницы раздела новостей:
HTML
<title>Новости — страница 4</title>
<description>Новости — страница 4</description>
<h1>Новости — страница 4</h1>
<link rel="canonical" href="https://site.ru/company/news/">
<link rel="next" href="https://site.ru/company/news/?PAGEN_1=5">
<link rel="prev" href="https://site.ru/company/news/?PAGEN_1=3">
Разберём каждый тег:
title с номером страницы — заголовок автоматически дополняется суффиксом «— страница N», что делает каждую страницу пагинации уникальной в поисковой выдаче и исключает дубли по title.
rel="canonical" — указывает на чистый URL раздела без GET-параметров. Это сигнал для поисковика: «главная версия этого контента — вот здесь», что защищает от склейки страниц пагинации с основной.
rel="next" — ссылает на следующую страницу (PAGEN_1=5), формируя цепочку вперёд.
rel="prev" — ссылает на предыдущую страницу (PAGEN_1=3), замыкая двустороннюю последовательность.

Таким образом поисковый робот видит не набор разрозненных URL, а единую логическую цепочку страниц одного раздела. Google объединяет ссылочный вес всей серии и, как правило, показывает в выдаче первую страницу как наиболее релевантную. Яндекс rel="prev/next" технически игнорирует, однако canonical и уникальные title отрабатывают и для него.