Play
online · 30 мин
Туториалы 25 мин чтения 22.09.2026

SEO-продвижение сайта: как мы выводим проекты в топ и что реально работает в 2025

Вывести сайт в топ в 2025 — это не про покупку ссылок, а про инженерную работу с данными. Показываем, как находим страницы, выпавшие из индекса, и превращаем семантику в реальный рост позиций.

Что в итоге сделаем: карта работ по SEO-продвижению сайта

Когда к нам приходят с задачей «вывести сайт в топ», первое, что мы делаем — не покупаем ссылки и не переписываем мета-теги на глаз. Мы смотрим, что уже есть: какие запросы реально приводят показы, какие страницы Google и Яндекс видят, а какие — нет. В проектах с большим каталогом на Bitrix картина почти всегда одна: часть товаров выпала из индекса после импорта, часть страниц отдаёт боту пустой каркас из-за AJAX-подгрузки, а данные о позициях годами никто не выгружал, потому что «руками долго». Это не проблема контента — это инженерная задача, и решается она кодом, а не набором текстов.

SEO-продвижение сайта в нашей практике — это последовательность действий от данных к правкам. Сначала собираем семантику и статистику из вебмастеров, потом чиним техническую базу, потом смотрим, как изменения отражаются в выдаче. Каждый шаг опирается на предыдущий: без семантики нечего оптимизировать, без вебмастера не видно, что сломалось, без мониторинга непонятно, помогло ли.

Ниже — карта из девяти шагов, которую мы прошли в этой статье. Она рассчитана на технического специалиста или владельца сайта, который хочет вести продвижение системно, а не платить за «SEO-пакеты» вслепую.

  1. Собрать семантику через API Яндекс.Вебмастера на PHP — получить реальные запросы, по которым сайт уже показывается.
  2. Выгрузить данные Search Console через Search Analytics API — увидеть клики, показы и позиции из Google.
  3. Подключить сайт к Яндекс.Вебмастеру прямо из админки Bitrix — без ручной привязки аккаунта.
  4. Разобрать ошибку загрузки ресурсов Google-ботом: «Другая ошибка» в Проверке URL.
  5. Разобраться, как индексируются AJAX-сайты и что делать с подгрузкой страниц.
  6. Понять, как правки в структуре сайта влияют на продвижение — что безопасно, а что роняет позиции.
  7. Проверить, попал ли сайт в каталоги Яндекс и Google, и как туда занести проект.
  8. Настроить мониторинг позиций и запросов без ручных выгрузок — чтобы данные приходили сами.
  9. Разобрать самые частые ошибки, которые ломают SEO-продвижение сайта, и как их не допустить.

Как собрать семантику через API Яндекс.Вебмастера на PHP

Типичный сценарий: каталог на 20 000+ SKU, десятки тысяч страниц под фильтры и теги, а семантику менеджеры собирают руками через выгрузку из веб-интерфейса Вебмастера. Пара сотен запросов — ещё терпимо. Но когда нужно снять ТОП запросов по 40 разделам и разложить их по кластерам, ручная выгрузка перестаёт масштабироваться: файлы не сводятся, даты расходятся, половина запросов теряется при копипасте. В проектах с большим каталогом мы решаем эту задачу программно — тянем данные из API Яндекс.Вебмастера прямо в БД сайта и уже там кластеризуем, сверяем с текущим ядром и подкладываем в мета-шаблоны. Это даёт сразу две вещи: живую картину спроса (по каким запросам сайт реально показывался за последнюю неделю) и основу для дальнейшей автоматизации — связку «запрос → посадочная → мета-тег» можно пересобирать по расписанию, а не раз в квартал. Дальше покажем, как это устроено на PHP: авторизация, получение `host_id`, вызов метода `host-search-queries-popular` и разбор ответа.

  1. Авторизоваться в Яндексе под аккаунтом, к которому привязан сайт, и подтвердить права на домен — через мета-тег, DNS-запись или файл в корне. Без подтверждённого доступа API вернёт ошибку прав, даже если OAuth-токен валиден.
  2. Получить OAuth-токен для API Яндекс.Вебмастера. Зарегистрируйте приложение в сервисе Яндекс.OAuth, запросите scope на работу с Вебмастером и обменяйте код авторизации на access-токен. Токен живёт ограниченное время — refresh-логику заложите сразу, иначе интеграция отвалится через несколько часов.
  3. Запросить `host_id` сайта и вызвать метод `host-search-queries-popular`. Сначала дёргаем метод списка хостов пользователя, находим нужный по домену и сохраняем его `host_id` — это составной идентификатор вида `https:example.com:443`, а не просто домен. Затем этим `host_id` вызываем метод популярных запросов.
PHP
<?php
// Получение ТОП поисковых запросов через API Яндекс.Вебмастера.
// Токен храните в .env или в опциях модуля — не в коде.
$token  = getenv('YANDEX_WEBMASTER_TOKEN');
$userId = getenv('YANDEX_WEBMASTER_USER_ID'); // ID пользователя из user-info
$hostId = 'https:example.com:443';            // составной host_id, не домен

$baseUrl = 'https://api.webmaster.yandex.net/v4/user/' . $userId . '/hosts/' . rawurlencode($hostId);

// 1. ТОП-500 запросов по числу показов за последнюю неделю.
$url = $baseUrl . '/search-queries/popular/'
. '?order_by=IMPRESSIONS'
. '&query_indicator=TOTAL_SHOWS'
. '&query_indicator=TOTAL_CLICKS'
. '&limit=500';

$ch = curl_init($url);
curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: OAuth ' . $token,
            'Accept: application/json',
        ],
        CURLOPT_TIMEOUT        => 30,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode !== 200) {
    // 401 — протух токен, 403 — нет прав на хост, 404 — неверный host_id.
    throw new RuntimeException('Webmaster API error: HTTP ' . $httpCode);
}

$data = json_decode($response, true);

// 2. Складываем в инфоблок / отдельную таблицу для кластеризации.
\Bitrix\Main\Loader::includeModule('iblock');
$el = new CIBlockElement();

foreach ($data['queries'] ?? [] as $row) {
    $query = $row['query_text'];
    $shows = 0;
    $clicks = 0;
    foreach ($row['indicators'] as $indicator) {
        if ($indicator['type'] === 'TOTAL_SHOWS')  { $shows  = $indicator['value']; }
        if ($indicator['type'] === 'TOTAL_CLICKS') { $clicks = $indicator['value']; }
    }

    $el->Add([
            'IBLOCK_ID' => 12, // замените на ID вашего инфоблока семантики
            'NAME'      => $query,
            'PROPERTY_VALUES' => [
                'SHOWS'  => $shows,
                'CLICKS' => $clicks,
                'CTR'    => $shows > 0 ? round($clicks / $shows * 100, 2) : 0,
            ],
    ]);
}

Ответ метода — это массив `queries`, где у каждого элемента есть `query_text` и список `indicators`. Ключевое место: индикаторы приходят массивом объектов, а не плоскими полями, поэтому в коде мы их раскладываем вручную. Именно на этом шаге чаще всего ломаются самописные интеграции — пытаются прочитать `$row['shows']`, которого в ответе нет. Если запрашиваете `limit=500`, получите до 500 записей с наибольшим числом показов за последнюю неделю; метод умеет отдавать и больше — вплоть до ТОП-3000 по данным справки Яндекса, но конкретный потолок зависит от версии API и от того, какие `query_indicator` вы запросили. На практике мы почти всегда стартуем с 500: этого хватает, чтобы закрыть основные коммерческие кластеры, а «хвост» добираем позже точечно.

Дальше данные уходят в БД — отдельная таблица или инфоблок, как в примере. Там уже удобно считать CTR, сортировать по показам, резать мусорные запросы регулярками и запускать кластеризацию (по вхождениям, по SERP, через TF-IDF — кому что ближе). Если проект на нескольких языках или с поддоменами, заводите `host_id` в отдельной колонке и группируйте выгрузки по нему, иначе запросы разных сайтов склеятся в одну кучу.

Грабля: передавать в URL метода домен вместо составного `host_id`. Многие подставляют `example.com` или `https://example.com` и получают пустой ответ или 404 — при этом HTTP-код может быть 200, и ошибка проходит незамеченной. Правильный `host_id` всегда составной: `https:example.com:443`. Берите его из метода списка хостов и сохраняйте в конфиг — не собирайте строку вручную, легко ошибиться со схемой или портом.

Как выгрузить данные Search Console через Search Analytics API

Семантика из Яндекс.Вебмастера, которую мы разбирали выше, — это только половина картины. Google показывает другие запросы, другие показы и другую динамику: у магазинов с англоязычными или смешанными аудиториями доля Google-трафика легко перевешивает, а по части коммерческих интентов выдача Яндекса и Google расходится настолько, что строить одну семантическую карту на двух источниках нельзя. У нас в практике типичная ситуация: маркетолог приносит отчёт из Яндекс.Вебмастера, а через месяц выясняется, что половина запросов с реальными конверсиями приходила из Google — и в исходной семантике их просто не было.

Search Analytics API решает эту задачу системно: вместо ручных выгрузок через интерфейс Search Console мы получаем те же данные — клики, показы, CTR и среднюю позицию — в виде JSON, который можно сложить в свою таблицу и сравнивать с Яндексом на одной оси. Дальше это комбинируется с аналитикой по страницам, с проверкой индексации и с мониторингом позиций — об этом в следующих секциях. А сейчас разберём, как настроить доступ и вытащить первый срез.

PHP

В коде три места, которые почти всегда приходится адаптировать под проект. Первое — авторизация: мы используем сервисный аккаунт, потому что он не требует интерактивного OAuth-редиректа и спокойно живёт в кроне. Альтернатива — OAuth от имени пользователя, если нужно обращаться к чужим ресурсам по поручению клиента; тогда вместо setAuthConfig() храните refresh-токен и передавайте его в клиент. Второе — $siteUrl: строка должна совпадать с тем, как ресурс заведён в Search Console, включая префикс sc-domain: для доменных ресурсов. Третье — setRowLimit() и setStartRow(): это параметры пагинации, о них ниже отдельно.

По диапазону дат правило жёсткое: API требует, чтобы startDate и endDate охватывали не менее одного дня. Ноль дней или пустой интервал вернут ошибку, а не пустой ответ. Мы обычно берём окно в 30 или 90 дней — так данные успевают «устояться», а свежие дни с неполной статистикой не портят агрегаты. Группировка задаётся массивом dimensions: query даёт запросы, page — URL, date — дневную разбивку, country и device — срезы по гео и устройствам. Комбинировать можно, но помните: каждая дополнительная размерность умножает число строк, и на каталоге с десятками тысяч запросов ответ легко упирается в лимиты. Для семантики мы берём ['query', 'page'], для динамики — ['date'], для разбора аномалий — ['query', 'date'].

Про пагинацию: сервер отдаёт порцию строк, а следующую порцию вы запрашиваете тем же запросом, увеличивая startRow на размер предыдущей порции, пока getRows() не вернёт пустой массив. Это единственный корректный способ обойти длинный хвост — иначе вы просто не увидите запросы за пределами первой страницы.

Грабля с лимитом строк. По умолчанию Search Analytics возвращает 1000 строк за запрос — это подтверждают и практические гайды по API. Если вы запрашиваете ['query', 'page'] по каталогу с длинным хвостом и не трогаете startRow, вы получите только первую тысячу и решите, что остальных запросов нет. Явно поднимайте rowLimit до максимума, который принимает API, и обязательно крутите цикл по startRow, пока приходят строки.

Как подключить сайт к Яндекс.Вебмастеру из админки Bitrix

Семантику через API Вебмастера мы разбирали выше — но любой метод вернёт пустой массив, пока сайт не привязан к аккаунту и права на домен не подтверждены. Без этой привязки API просто не отдаст данные: ни host-search-queries-popular, ни query-analytics/list, ни динамику. Приходится вручную выгружать CSV из веб-интерфейса, склеивать периоды в Excel и молиться, чтобы менеджер не забыл скачать отчёт в пятницу. В проектах с регулярной аналитикой это первый шаг, который окупается сразу: один раз привязали — и дальше все скрипты мониторинга позиций работают без ручных выгрузок.

Хорошая новость в том, что в Bitrix для этого есть штатный механизм — не нужно ни писать OAuth-обвязку руками, ни лезть в консоль разработчика Яндекса. Всё делается из админки за пару минут.

  1. Авторизоваться в Яндексе под тем аккаунтом, который будет владельцем сайта в Вебмастере. Если у клиента уже есть рабочий аккаунт с другими проектами — используйте его, а не заводите новый: иначе потом придётся переносить права.
  2. Открыть в админке Bitrix страницу «Маркетинг → Поисковая оптимизация → Поисковые системы → Яндекс». Именно эта страница служит для подключения сайта к сервису Яндекс.Вебмастер — не путайте с настройками модуля «Поиск».
  3. Нажать «Привязать сайт», пройти авторизацию и подтвердить права на домен — тем способом, который предложит Яндекс (мета-тег, DNS-запись или файл в корне). После подтверждения Bitrix сохранит привязку и будет использовать её для всех обращений к API.

Дальше — момент, на котором спотыкаются чаще всего. Если сайт уже привязан к аккаунту другого человека (бывший подрядчик, старый вебмастер, предыдущая студия), повторная привязка не сработает: Вебмастер не даст двум аккаунтам подтвердить один и тот же домен. Вариантов два — запросить доступ к существующему аккаунту (Яндекс даёт такую возможность через «Управление доступом», если владелец согласен) или перенести права на свой аккаунт, попросив прежнего владельца удалить сайт из своего списка. Первый путь предпочтительнее: история и накопленная статистика сохраняются. Второй — когда связаться с прежним владельцем невозможно, и тогда данные начинают собираться заново.

У нас в практике отдельная головная боль — проекты, где сайт привязан к личному аккаунту уволившегося сотрудника. Поэтому мы всегда настаиваем на корпоративном аккаунте Яндекса с доступом для команды: однажды это спасает недели переписки с поддержкой.

Как исправить ошибку загрузки ресурсов Google-ботом: «Другая ошибка» в Проверке URL

Открываешь отчёт «Проверка URL» в Search Console или запускаешь тест на мобильную совместимость — и вместо привычного «Страница доступна» видишь расплывчатое «Другая ошибка». Рядом подгружается скриншот, который снял сам Google: страница без стилей, картинки товаров не отрисовались, вёрстка разъехалась. При этом сайт открывается в браузере мгновенно, сервер на BitrixVM, ресурсов с запасом, ошибок в логах нет. У нас в практике это одна из самых раздражающих ситуаций: непонятно, что чинить, потому что непонятно, где ломается. Инструмент не сообщает ни код ответа, ни имя ресурса, который не загрузился, — просто «другая». Мы регулярно видим эту картину у магазинов с большим каталогом на Bitrix, где шаблон каталога тянет десятки картинок и несколько CSS/JS-файлов ещё до первого рендера. Хорошая новость: в большинстве случаев проблема решается не на стороне сервера, а на стороне шаблона — и ниже разберём, что именно менять.

Разберёмся, почему Google-бот вообще спотыкается на странице, которую браузер пользователя грузит без проблем.

При проверке страницы робот экономит свои ресурсы: время и трафик. Он не ждёт бесконечно, пока подгрузятся все ресурсы, а обрывает загрузку по своему внутреннему лимиту. Если HTML-код длинный, а ресурсы тяжёлые или их много, бот успевает получить только часть — и фиксирует «Другую ошибку».

То есть сервер отдаёт всё корректно, но к моменту, когда бот решает «хватит», стили и картинки ещё не пришли. Отсюда и пустой скриншот без оформления.

Что это значит на практике:

  • увеличивать мощность сервера или чинить nginx — бессмысленно, узкое место не там;
  • проверять надо не «отвечает ли сервер 200», а «успевает ли бот получить ресурсы до своего таймаута»;
  • работать нужно с весом и количеством ресурсов, которые грузятся на первом экране.

Что реально помогает: короткий HTML и лёгкие ресурсы

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

Третье: крупные сайты решают это через CDN — робот выделяет дополнительные ресурсы на сканирование ресурсов со стороннего домена, и они успевают загрузиться в срок. Плюс стили лучше отдавать не отдельным файлом, а инлайн-кодом в <head> — тогда они приходят вместе с HTML и не требуют отдельного запроса.

Четвёртое, и у нас это чаще всего и оказывалось решающим: сократить число ресурсов, которые грузятся на первом экране. Ниже — про картинки каталога.

Как мы сокращаем lazy-load картинок в каталоге

В разобранном случае решающим оказалось уменьшение числа картинок, которые подгружаются через lazy-load при первом посещении. Для пользователя это незаметно (lazy-load как раз для этого), но бот Google воспринимает каждую подгрузку как отдельный ресурс в общем бюджете.

По разбору это дало экономию 120–200 КБ и 100–200 мс — и «Другая ошибка» практически полностью ушла (исключения бывают, но редко).

В шаблоне компонента каталога это правится в одном месте — там, где формируется список первых элементов для немедленной загрузки. Показываем на PHP:

PHP
350, 'height' => 350],
BX_RESIZE_IMAGE_PROPORTIONAL
);
?>

Замените $eagerCount на своё значение, если у вас другая плотность сетки или размер картинок. Для каталога с крупными превью 6 — разумный старт; для мелких плиток можно поднять до 8.

Грабля: увидев совет «инлайнить CSS в <head>», легко засунуть туда весь файл стилей целиком. Не делайте так: HTML раздуется выше тех самых 1000 строк, и вы вернётесь к исходной проблеме с другой стороны. Инлайните только критичный CSS — стили первого экрана (шапка, сетка каталога, кнопки). Остальное оставляйте отдельным файлом с обычным подключением.

Как индексируются AJAX-сайты: что делать с подгрузкой страниц

Самый частый запрос на эту тему звучит так: «у нас блог (или каталог) с бесконечной подгрузкой — не потеряем ли мы позиции, если оставим AJAX?» Опасение справедливое. AJAX сам по себе не запрещён и не наказывается, но он легко создаёт ситуацию, когда поисковый робот физически не видит контента, который видит пользователь. В проектах с большим каталогом и активным импортом мы разбираем эту задачу почти на каждом SEO-аудите: сначала проверяем, отдаётся ли контент при прямом заходе на URL, потом смотрим, есть ли у каждой «подгружаемой» страницы собственный адрес, и только после этого трогаем фронтенд. Порядок важен. Если поменять местами, можно полгода полировать скорость подгрузки, пока половина каталога не в индексе.

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

Поисковый робот не исполняет ваш JavaScript как браузер пользователя. Точнее, исполняет — но с задержкой, с ограниченным бюджетом на рендер и без гарантии, что дождётся ответа вашего AJAX-запроса. Если контент появляется только после клика по кнопке «Показать ещё» или после скролла, робот может уйти со страницы, так и не увидев товаров, статей и текстов. Для него это будет пустой шаблон с пустым контейнером.

Отсюда два требования, которые мы проверяем в первую очередь:

  • Отдельный URL для каждой логической страницы. Не #hash, а нормальный путь вида /catalog/elektronika/page-2/, который открывается при прямом заходе.
  • Серверный рендер (SSR) или пререндер. При заходе на этот URL сервер должен отдать готовый HTML с контентом — без ожидания JS. Иначе робот получит пустую оболочку.

Если хотя бы одно условие не выполнено, страницы за пределами первой либо вообще не попадут в индекс, либо попадут с «тонким» контентом. На Bitrix это решается комбинацией двух слоёв: сервер отдаёт полноценную страницу по прямому URL, а AJAX-запросы к тому же URL возвращают только фрагмент для бесшовной подгрузки. Один и тот же роутинг, разный ответ в зависимости от заголовка X-Requested-With.

Второй слой — заставить браузер менять адресную строку при AJAX-переходах. Именно здесь работает history.pushState(): он подменяет URL без перезагрузки страницы, и этот URL можно скопировать, отправить в мессенджер или отдать роботу — сервер по нему отдаст тот же контент.

JS
// Перехватываем клики по ссылкам пагинации и категорий
document.addEventListener('click', (e) => {
    const link = e.target.closest('a[data-ajax-nav]');
    if (!link) return;

    const url = link.href; // реальный URL, например /catalog/page-2/
    e.preventDefault();

    fetch(url, {
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        })
        .then((res) => res.text())
        .then((html) => {
            // подменяем контейнер, а не всю страницу
            document.querySelector('#catalog-list').innerHTML = html;

            // меняем URL в адресной строке — без перезагрузки
            history.pushState({
                path: url
            }, '', url);

            // обновляем title и meta для корректного шаринга
            document.title = link.dataset.title || document.title;
        });
});

// Обработка кнопок «назад/вперёд» браузера
window.addEventListener('popstate', (e) => {
    const path = e.state?.path || location.pathname;
    fetch(path, {
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        })
        .then((res) => res.text())
        .then((html) => {
            document.querySelector('#catalog-list').innerHTML = html;
        });
});

Ключевое здесь — pushState меняет URL, но не создаёт серверный ответ. Если по этому URL сервер отдаёт ту же пустую оболочку, что и раньше, ничего не изменится. Потому pushState и SSR идут парой: первый даёт адрес, второй — контент по этому адресу.

Грабля: ссылки пагинации и категорий ведут на #hash или на javascript:void(0), а контент подгружается через AJAX без серверного рендера. Для пользователя всё работает, для робота — это одна страница с меняющимся содержимым. В индекс попадёт только первый экран, остальные страницы каталога выпадут. Позиции просядут по всем запросам, которые раньше закрывались этими страницами, а конкуренты получат трафик. Обход: заменить hash-ссылки на реальные URL, настроить серверный рендер по этим URL и только после этого включать pushState для бесшовного перехода.

Как правки в структуре сайта влияют на продвижение

Вопрос, который всплывает почти в каждом проекте после первой волны технических правок: «Мы поменяли структуру через месяц после старта — не упадёт ли выдача?». Опасение понятное: страницы только-только начали индексироваться, позиции по семантике из Вебмастера и Search Console (мы разбирали их в секциях выше) ещё нестабильны, а тут перестановка разделов, новые URL, редиректы. Кажется, что любой чих в структуре откатит накопленный прогресс.

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

Поисковики при индексации смотрят только на фронтенд — то, что отдаётся в ответ на запрос. Валидная вёрстка, корректная разметка, читаемая структура заголовков, осмысленные анкоры внутренних ссылок. Это то, что напрямую влияет на ранжирование и на то, как Google и Яндекс понимают содержимое страницы.

Бэкенд влияет косвенно, но через важные вещи:

  • Скорость отдачи страницы: медленный рендер бьёт по поведенческим и по краулинговому бюджету.
  • Редиректы: цепочки из трёх-четырёх хопов размывают вес и тратят лимит обхода.
  • Вредоносный код: сторонние вставки и заражённые скрипты роняют доверие домена целиком.

Именно поэтому правки на уровне «переехали файлы, поменяли классы, отрефакторили контроллеры» для выдачи почти невидимы. А вот изменение того, что видит пользователь и робот в HTML, уже влияет.

Что усиливает позиции после структурных правок:

  • Новые категории с проработанными SEO-текстами, заголовками и перелинковкой — это расширение посадочных под семантику.
  • Соблюдение семантического ядра при формировании структуры разделов и подразделов — каждая сущность получает свою страницу.
  • Аккуратные H1–H2, осмысленные URL, отсутствие дублей и «мусорных» листингов.

Что вредит:

  • Хаотичные правки без цели — «переставим разделы, потом разберёмся».
  • Смена URL без 301-редиректов — старые адреса выпадают, новые не подхватываются.
  • Массовое переименование сущностей в один день — краулер не успевает переобойти всё.

Как проверить, что сайт попал в каталоги Яндекс и Google

Распространённая ошибка: путать каталог с поисковой выдачей. Когда владелец сайта говорит «мы попали в Яндекс» — он обычно имеет в виду, что страницы проиндексировались и показываются по запросам. Это одно. А попадание в тематический каталог — отдельная история: ручной список сайтов, куда редакторы отбирают проекты по значимости. В наших проектах этот вопрос всплывает редко, но если у клиента есть задача засветиться в каталоге, важно понимать: это не техническая настройка, а заявка, которую рассматривает живой человек. И да, каталог Google — не поиск Google, а DMOZ (Open Directory Project), отдельный проект с собственной редакцией.

В этой секции разберём, как подать сайт и как проверить, что он уже там.

  1. Заполнить форму в каталоге Яндекса и отправить заявку. Открываем страницу каталога, находим подходящий раздел по тематике сайта, заполняем форму: адрес, название, описание, контакты. Отправляем — дальше ждём решения редактора. Автоматического «приёма» здесь нет, статус заявки не отображается в панели вебмастера.
  2. Подать сайт в DMOZ (каталог Google). Если форма подачи на dmoz.org доступна — выбираем ветку, максимально близкую к тематике, и заполняем заявку по тем же принципам. Форма периодически закрывается на профилактику или модерацию бэклога — это нормальная ситуация, не баг. Проверяем доступность формы перед тем, как обещать клиенту подачу.
  3. Проверить наличие сайта в каталоге поиском по домену. Самый надёжный способ — открыть каталог и поискать домен через встроенный поиск или вручную пройти по ветке. Если сайт есть — он найдётся; если нет — не найдётся. Никаких API и «проверок статуса» у каталогов нет, только ручной поиск по названию или URL.

Отдельный сценарий: сайт может попасть в каталог и без вашей заявки. Редакторы просматривают интернет самостоятельно и добавляют проекты, которые считают исключительно значимыми. Это не правило и не гарантия, а редкое исключение: рассчитывать на него как на канал продвижения нельзя. Если редакторы добавят сайт сами, они уведомят об этом — обычно письмом на контактный адрес из WHOIS или с сайта.

Практический вывод: каталоги не двигают позиции. Это скорее гигиенический пункт для репутации и трафика из тематических подборок. Если задача клиента — «попасть в каталог Яндекс и Google», закрываем её заявкой и проверкой, но не ждём от неё SEO-эффекта. Основную работу делают индексация, семантика и техническая доступность — то, что мы разбирали в предыдущих секциях.

Как мониторить позиции и запросы без ручных выгрузок

Самый частый запрос на эту тему у владельцев: «Мы сделали правки, а как понять, что они сработали?» Пока коммерческие показатели не сдвинулись — а это, как правило, недели, а не дни — единственный объективный сигнал, что SEO-работа идёт в нужную сторону, это динамика запросов и позиций. Ручная выгрузка из Вебмастера и Search Console раз в месяц не годится: за это время успевает накопиться целый пласт изменений, и непонятно, какая правка дала плюс, а какая — просадку.

Мы в своей практике всегда ставим сбор на cron: раз в сутки скрипт забирает свежий срез по запросам, кладёт его в отдельную таблицу, а дальше по этой таблице строится график динамики. Это даёт две вещи. Во-первых, видно тренд по конкретным запросам — какие растут, какие стагнируют, какие выпали из показа. Во-вторых, при разборе просадок есть к чему привязаться: сравнить график с датой релиза и понять, что именно повлияло. Комбинируется такая автоматизация с уже собранной семантикой из Яндекса и Google — как мы разбирали в предыдущих секциях, — и превращает разрозненные выгрузки в единую картину по проекту.

Инструментов под эту задачу три, и они не заменяют, а дополняют друг друга. API Яндекс.Вебмастера — основной источник по Яндексу: метод query-analytics/list отдаёт дневную разбивку по запросам, history — динамику показателей, а сводный search-queries/popular даёт до 500 записей с наибольшим числом показов. Для ежедневного мониторинга нам нужен именно query-analytics/list — он даёт срез по дням, из которого и собирается график. По Google работаем через Search Analytics API из Search Console: метод query возвращает строки, сгруппированные по заданным ключам, обязательно с диапазоном дат не менее одного дня.

Важный нюанс: если в группировку включена дата, дни без данных из выдачи просто опускаются. Это не значит, что показов не было, это значит, что их не зафиксировано, и в графике такие дни нужно обрабатывать отдельно, иначе линия будет рваться.

Третий инструмент — URL Inspection tool — не для массового сбора, а для точечной проверки: когда видим просадку по конкретной странице, прогоняем её через инспектор и смотрим, что с обходом, индексацией и показом именно у неё. В коде ниже — cron-задача, которая раз в сутки забирает запросы из API Вебмастера и пишет их в таблицу для графика.

PHP
<?php
// /local/cron/seo_positions.php — запускать раз в сутки по cron
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';

use Bitrix\Main\Application;
use Bitrix\Main\Web\HttpClient;

const YANDEX_TOKEN = 'y0__ваш_oauth_токен'; // замените на ваш токен
const HOST_ID      = 'https:example.ru:443';  // составной host_id, не просто домен

$connection = Application::getConnection();
$http = new HttpClient(['socketTimeout' => 30]);

// 1. Забираем дневную разбивку по запросам за вчера
$date = (new DateTime('yesterday'))->format('Y-m-d');
$url = 'https://api.webmaster.yandex.ru/v4/user/'
. YANDEX_TOKEN . '/hosts/' . rawurlencode(HOST_ID)
. '/query-analytics/list';

$http->setHeader('Authorization', 'OAuth ' . YANDEX_TOKEN);
$http->setHeader('Content-Type', 'application/json');

$response = $http->post($url, json_encode([
            'offset'         => 0,
            'limit'          => 500,
            'text_indicator' => 'QUERY',
            'indicators'     => ['TOTAL_SHOWS', 'TOTAL_CLICKS'],
            'date_from'      => $date,
            'date_to'        => $date,
]));

$data = json_decode($response, true);

if (empty($data['textIndicators'])) {
    AddMessage2Log('SEO: пустой ответ API Вебмастера за ' . $date);
    return;
}

// 2. Пишем срез в таблицу для графика динамики
foreach ($data['textIndicators'] as $row) {
    $query  = $connection->getSqlHelper()->forSql($row['name']);
    $shows  = (int)($row['indicators']['TOTAL_SHOWS'] ?? 0);
    $clicks = (int)($row['indicators']['TOTAL_CLICKS'] ?? 0);

    $connection->queryExecute("
        INSERT INTO seo_query_dynamics (query_text, stat_date, shows, clicks)
        VALUES ('{$query}', '{$date}', {$shows}, {$clicks})
        ON DUPLICATE KEY UPDATE shows = {$shows}, clicks = {$clicks}
        ");
    }

    AddMessage2Log('SEO: записано ' . count($data['textIndicators']) . ' запросов за ' . $date);

Что здесь стоит поменять под свой проект: limit при большом семантическом ядре придётся увеличить и ходить постранично через offset, а host_id брать не из головы, а из ответа метода со списком сайтов. Частая ошибка интеграции: передают домен вместо составного идентификатора и получают пустоту. Если запросов меньше сотни, можно обойтись без отдельной таблицы и писать прямо в инфоблок. Для мультирегиональных проектов имеет смысл добавить в группировку регион и складывать срезы отдельно.

Самые частые ошибки, которые ломают SEO-продвижение сайта

Если собрать статистику по обращениям «сайт не растёт в поиске», картина повторяется из проекта в проект. Дело почти никогда не в ссылках и не в «алгоритмах», а в инженерных мелочах, которые кто-то однажды не проверил. Мы разбирали это выше по каждой теме отдельно: семантика, выгрузки, индексация, AJAX, структура. Здесь — сводка грабель, на которые наступают чаще всего.

Ошибки делятся на три группы. Первые ломают сбор данных: пока домен путают с host_id, а из Search Analytics берут только первые 1000 строк, вся аналитика строится на неполной картине. Вторые ломают индексацию: AJAX без URL, инлайн всего CSS в <head>, слишком тяжёлые страницы. Третьи ломают уже достигнутый результат — правки структуры без пересборки семантики.

Ниже — что именно происходит и как это обойти.

Грабля Причина Решение
Домен вместо host_id в API Вебмастера API v4 ждёт составной идентификатор вида https:example.ru:443, а не голый домен Получить список сайтов через метод user и подставлять host_id из ответа
Потеря длинного хвоста Search Analytics по умолчанию отдаёт 1000 строк за запрос Пагинация через startRow до пустой выдачи
AJAX без URL Подгрузка контента не меняет адрес — робот видит одну страницу History API + серверный рендер по прямому URL
Инлайн всего CSS HTML раздувается, Google-бот экономит ресурсы и бросает загрузку Вынести стили в файл, критичный CSS — инлайном, остальное — CDN
Правки без семантики Структуру поменяли, ядро не пересобрали — новые страницы никто не оптимизировал После каждой смены структуры — повторный сбор запросов и маппинг на URL

Разобранные случаи на ru.stackoverflow: ru.stackoverflow, ru.stackoverflow.

Читайте также: Как ускорить Битрикс: композит, кэширование и настройка сервера, Обмен с сайтом 1С Битрикс: пошаговая настройка узла и импорта.

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

Можно ли собирать семантику через API Яндекс.Вебмастера, если сайт ещё не подтверждён в панели?

Нет, метод hostInfo и statistics требуют подтверждённых прав на хост через verification или DNS-запись. До верификации доступен только публичный Wordstat API, но он даёт частотность без привязки к вашему домену.

Что делать, если Search Analytics API возвращает 403 при выгрузке запросов?

Проверьте, что сервисный аккаунт добавлен в Search Console как пользователь с правами «Полный доступ», а не «Ограниченный». В запросе обязательно указывайте siteUrl в формате sc-domain:example.com или https://example.com/ — формат должен совпадать с тем, что в панели.

Чем отличается проверка URL в Search Console от отчёта «Ошибки сканирования» в Яндекс.Вебмастере?

Проверка URL показывает статус конкретного адреса в реальном времени и рендеринг Googlebot, а «Ошибки сканирования» — накопленную статистику по всему хосту за период. Для диагностики «Другой ошибки» используйте именно Проверку URL: она покажет код ответа и загруженные ресурсы.

А если у меня SPA на React, а не Bitrix — AJAX-секция статьи мне подходит?

Принципы те же: нужен серверный рендеринг или prerender для ботов, а также корректные canonical и History API вместо hash-навигации. Для Google достаточно динамического рендеринга, но Яндекс стабильнее индексирует SSR.

Можно ли мониторить позиции без платных сервисов вроде Topvisor?

Да, через API Яндекс.Вебмастера (метод search-queries) и Search Console (searchanalytics.query) можно выгружать показы, клики и среднюю позицию по запросам в свою БД. Ограничение — данные с задержкой 2–3 дня и без точной позиции в топ-10 по каждому ключу.

Что делать, если после правок в структуре сайта позиции просели?

Проверьте в Вебмастере и Search Console, не появились ли 404 на старых URL, и настройте 301-редиректы на новые адреса. Если просадка длится больше двух недель, откатите изменения через бэкап и переиндексируйте раздел через «Переобход страниц».

#SEO #Bitrix
автор · Backend / SRE Engineer
Артем Колячек

Backend-разработчик и SRE в студии Paradigma. Занимается тем, что у других проектов обычно обнаруживается за неделю до запуска: переездом Bitrix-проектов между серверами, починкой кешей после миграций, выстраиванием pipeline для контейнеров, мониторингом под нагрузкой.

До Paradigma — 7 лет в backend (PHP + Postgres) и в operations (Linux, Docker, Coolify, restic-бэкапы). Любит когда логи разговаривают полным синтаксисом ошибки, а не «что-то пошло не так».

На блоге пишет ровно про те ситуации с которыми сам разбирался руками: какой Bitrix-апдейт сломал кеш и как откатить, почему Docker-сеть не находит контейнер после рекрейта, что делать когда asyncpg ругается на event loop.