Кейс: 194 страницы в Яндексе и +86% кликов GSC за 50 дней — SEO в B2B-клининге без бюджета на ссылки

Кейс: 194 страницы в Яндексе и +86% кликов GSC за 50 дней — SEO в B2B-клининге без бюджета на ссылки

Кратко. 22 марта 2026 я выкатил новый сайт клининговой компании, в которой работаю руководителем отдела маркетинга. Через 50 рабочих дней — 194 страницы в индексе Яндекса, в Google Search Console +86% кликов и +90% показов за месяц, Lighthouse Performance 93/100 на мобильных, CLS 0.002. Без единой покупной ссылки и без агентства. Сайт самописный на PHP 8.4, 245 URL в карте.

Формальности

  • Автор: Сербун Дмитрий, маркетолог-руководитель, dimaserbun.ru
  • Профиль на VC: vc.ru/dimik236
  • Telegram для вопросов: @dimik90
  • Роль в кейсе: руководитель отдела маркетинга (РОМ) в клининговой компании СПб, всё руками — SEO, контент, разработка, аналитика, AmoCRM-интеграция
  • Период кейса: 22 марта — 7 мая 2026, ≈ 290 коммитов за 50 рабочих дней
  • Имя компании, домены, имена клиентов, доступы и реквизиты — намеренно скрыты по NDA с работодателем

Суть кейса

С нуля за 50 рабочих дней:

  • 245 URL в sitemap: 51 услуга, 18 районов СПб, 70 страниц метро, 12 городов Ленобласти, 50+ блог-статей
  • 194 страницы в Searchable Яндекса (на старте 13 апреля было 111, на 5 мая — 194-201)
  • 92 страницы вернул из «Excluded by noindex» Яндекса в индекс через переобход API + IndexNow + lastmod
  • +86% кликов и +90% показов в Google Search Console за месяц (52 → 97 кликов, 2 826 → 5 359 показов)
  • Lighthouse mobile: Performance 93, SEO 100, CLS 0, LCP 2.4 c, TBT 60 мс
  • Внутренний SEO Health Score 62 → 84 за 18 дней по аудиту 7 параллельных субагентов
  • 0 ₽ на покупные ссылки, 0 ₽ на SEO-агентство, 0 фрилансеров

В этой статье — что я делал по неделям, какие 5 правок дали наибольший вклад в метрики и где наступал на грабли.

Контекст и задача

У компании уже был сайт. Старый, тяжёлый, на типовом конструкторе, с проблемами CLS 0.377, дублями LocalBusiness Schema на 30+ страницах, без llms.txt, без AI-краулеров в robots.txt и с 43 страницами тонкого контента в sitemap. Полугодовая миграция на «модный» стек = риск потерять то, что уже ранжируется. Нужен был быстрый старт с прямым контролем над каждым байтом разметки и каждой строчкой schema.

Задача: за 1-2 месяца вывести новый сайт на стабильный поток органики из Яндекса и Google, без бюджета на ссылочное и без подрядчиков. Целевые сегменты — B2B-клининг (промышленный, медицинский, общепит, гостиницы, магазины) и районный B2C (генеральная, после ремонта, химчистка по СПб и ЛО).

Гипотеза

В локальной нише с большим количеством типовых запросов («уборка квартир + район», «клининг + метро», «химчистка + город») главный рычаг — programmatic SEO с порогом уникальности страницы > 45%. Шаблон один, контент уникальный, и блог-кластер на 50+ статей закрывает long-tail. Schema + AI-краулеры + llms.txt подключают AI Overviews и LLM-цитирования параллельно с обычной выдачей.

Гипотеза подтвердилась: 100 программатик-страниц + 50 блог-статей дали +86% кликов GSC за месяц без покупных ссылок.

Стек и архитектура

Намеренно простой, без CMS:

  • PHP 8.4 на shared-хостинге
  • .htaccess как роутер: /uborka-metro-{slug}/ → uborka-metro-{slug}.php, /uborka-{rajon}-rajon/ → uborka-{rajon}-rajon.php
  • MySQL: 5 таблиц — leads (заявки), blog_posts, blog_comments, reviews (рейтинги платформ для AggregateRating), mentions (каталоги)
  • PDO singleton в includes/db.php, utf8mb4, ERRMODE_EXCEPTION
  • CSS: style.css + style.min.css (versioned ?v=N) + critical inline в header.php
  • JS: js/main.js через defer, без сборки и без транспилятора
  • Шрифты: self-hosted Inter (woff2), без Google Fonts
  • Аналитика: GA4 + Яндекс Метрика + Roistat — все три через requestIdleCallback(fn, {timeout: 3000})
  • Формы: send.php → mail() + Telegram Bot API + AmoCRM API + БД (4 канала, 100% сохранения лидов при падении одного)

Почему так: скорость деплоя rsync, нет плагин-сюрпризов, прямой контроль над schema, отсутствие npm-зависимостей, которые завтра окажутся CVE.

Реализация по неделям

Неделя 1-2: техника и формы

Закрыл антиспам форм без капчи. Шесть фильтров:

  • Honeypot — скрытое поле <input type="text" name="website" style="display:none">
  • Regex-фильтр URL в комментарии (http://, https://, www.)
  • Лимит длины телефона ≥ 10 ≤ 11 цифр
  • Регресс на нелатиницу — комментарий > 5 символов без кириллицы блокируется
  • Rate-limit 5 запросов в час на IP по сессии и БД
  • Логирование в spam_log.txt, файл закрыт от веба через <Files> в .htaccess

CSP с whitelist для GA4, Метрики, Roistat, AmoCRM, CDN. HSTS preload. Referrer-Policy. X-Frame-Options. CSRF-токены на всех POST-формах.

Self-hosted Inter с @font-face size-adjust: 107%; ascent-override: 90% на базе Arial — это устранило FOUT-сдвиг и спасло CLS. После этих правок CLS упал с 0.377 до 0.002.

Аналитика — async через requestIdleCallback. На preconnect для счётчиков идти не стал: соединение истекало до использования, тратили ресурсы впустую. Хватило dns-prefetch для GTM и Метрики.

Неделя 3: первый аудит и сборка карты

5 апреля прогнал сайт через 7 параллельных субагентов:

  • Контент / E-E-A-T (вес 23%)
  • Технический SEO (22%)
  • On-Page (20%)
  • Schema (10%)
  • Core Web Vitals (10%)
  • AI Search Readiness (10%)
  • Изображения (5%)

Score 62/100. Главные дыры:

  • Schema: LocalBusiness дублировался на 30+ страницах, BreadcrumbList с якорями #services ломал валидацию в 36 файлах
  • DOM XSS в js/main.js — innerHTML с user input
  • MD5 пароля админки
  • 43 страницы метро с тонким контентом ~230 слов в sitemap

К 8 апреля закрыл всё:

  • LocalBusiness multi-type (LocalBusiness + ProfessionalService + CleaningService) — один блок в footer с глобальным @id. На страницах услуг — @id-ссылка на тот же объект, без дублей
  • BreadcrumbList: убрал якоря # в 36 файлах, валидация в Rich Results Test чистая
  • XSS поправил через createElement + textContent
  • Пароль админки на SHA-256 + salt
  • 43 тонких страницы метро убрал из sitemap, генератор generate-sitemap.php подтягивает реальный lastmod через filemtime()

К 10 апреля Score 72/100.

Параллельно прогнал сайт через Screaming Frog. 474 URL в краулере, из них 245 целевых, остальное — устаревшие версии и шаблонные заглушки.

Неделя 4: programmatic SEO — 100 страниц

16 апреля посвятил программатик-кластеру: 39 метро + 18 районов + 12 городов Ленобласти. Шаблон один (metro-template.php, district-template.php, lo-template.php), контент уникальный — $metro_content, $district_content, $city_content подставляются в общий каркас.

Чтобы не получить doorway-риск, на каждой странице:

  • Уникальный H1 и H2 (без шаблонных «Уборка в районе X»)
  • Раздел «Особенности района / метро / города» — 200-300 слов под локацию
  • 3-5 FAQ под локацию
  • Цены, время выезда бригады, ближайший объект компании в этой локации
  • AreaServed массив из 31 City в LocalBusiness schema

Уникальность страницы проверял через Screaming Frog «Near Duplicates» — порог > 45%. Programmatic SEO Score по моей внутренней методике 95/100.

klining-kolpino.php, klining-pushkin.php, klining-peterhof.php — каннибализация с районами СПб, отключил через 301 редирект на районы и перенёс контент в $district_content.

Неделя 5: блог-волна

9 апреля — первая волна, 21 статья за день. 17 апреля — вторая, 10 статей. К концу апреля в блоге 50+ статей по 1500-4000 слов.

Каркас каждой статьи:

  • BlogPosting + FAQPage + QAPage schema
  • Author Person с rel="author" на dimaserbun.ru
  • 4-5 FAQ — реальные запросы из Wordstat и People Also Ask
  • 3+ внутренних ссылок: 1 на услугу, 1 на другую статью, 1 на /price/ или /kontacty/
  • Pexels-баннер (свободная лицензия), для Дзена отдельная JPEG-версия — Дзен с WebP работает нестабильно
  • Q&A в комментариях — 38 пар «вопрос — ответ от лица компании», автогенерация QAPage из blog_comments WHERE reply_to IS NOT NULL с одобрением и IndexNow ping

Темп после двух волн — 2-3 статьи в неделю. Не публиковать больше 5 за день и не бэкдейтить 50 в один месяц — это сжигает шаблоны фильтров поисковиков. 6 мая распределил 11 готовых постов по диапазону 20-30 апреля, чтобы плотность публикаций выглядела ровно.

Неделя 6: AI-цитируемость и возврат из excluded

С 24 по 26 апреля три направления параллельно.

Возврат из «Excluded by noindex». 13 апреля у меня в Яндексе было 56 страниц в Excluded, 22 апреля — уже 92. Половина сайта. Через Yandex Webmaster API → /recrawl/queue отправил 37 блог-статей на переобход (квота 620 URL/сутки) + IndexNow ping для 70 метро-URL → HTTP 202 от Yandex и Bing. К 5 мая в Excluded осталась 1 страница из 93. Большинство вернулось в Searchable, теперь 194-201.

GEO-аудит. Score по платформам:

  • Google AI Overviews — 58
  • ChatGPT (web search) — 48
  • Perplexity — 62
  • Bing Copilot — 64
  • Итого 61/100

Главные правки:

  • Пассажи 134-167 слов под H2 на 5 ключевых страницах с прямым ответом в первых 40-50 словах + класс .answer-block для speakable
  • H2 в форму вопроса: «Какая уборка подходит для вашего типа квартиры?» вместо «Уборка по типу квартиры»
  • AI-краулеры в robots.txt: GPTBot, OAI-SearchBot, ClaudeBot, anthropic-ai, Google-Extended, PerplexityBot, CCBot, YouBot, cohere-ai, Bytespider, meta-externalagent
  • llms.txt в корне сайта по формату llmstxt.org — H1, blockquote-описание, разделы H2 со списками

Глоссарий. 26 апреля — /glossary/ на 45 терминов клининга + DefinedTermSet schema. Перелинковка 188 ссылок на /glossary/#anchor из текстов сайта — структурный сигнал «у сайта есть собственный словарь терминов» и дорожка для AI-цитирований.

Неделя 7: уровень А и Б, расширение шаблонов

5 мая — пакет правок на доверие и YMYL.

Уровень А (доверие): preconnect к домену CDN, mobile preload hero-картинки, динамический AggregateRating из MySQL в schema (формула взвешенного среднего по платформам Яндекс Карты, 2ГИС, Zoon, Yell), AreaServed массив из 31 City. Бренды чужих производителей убрал из тела страниц — Karcher, Koch Chemie и подобное — оставил только в местах, где они нужны как факт.

Уровень Б (YMYL): на страницах про уборку после смерти и после пожара — ссылки на лицензию Роспотребнадзора, СанПиН 3.3686-21, 1.2.3685-21, 2.1.3684-21 с гиперссылками на consultant.ru. Для YMYL-ниш это критично: Google и Яндекс жёстко режут страницы без авторитетного подтверждения.

6 мая — расширение шаблонов uborka-N-komnatnoj через SERP-анализ конкурентов: 1 500 → 1 700-1 900 слов, FAQ 5 → 10. Расширение шаблона метро: FAQ 3 → 5, секция «Подготовка», SEO-text. Переобход 70 URL через IndexNow.

7 мая — сводная проверка GSC и Яндекс Вебмастера. 39 URL отправил в recrawl, поправил RSS-фид Дзена (убрал дубль H1, конвертация table в списки).

Главные технические решения с самым большим вкладом

1. Один блок LocalBusiness multi-type с @id

Вместо 30+ дублей разной полноты — один объект в footer, всё остальное — @id-ссылки на него:

{ "@type": ["LocalBusiness", "ProfessionalService", "CleaningService"], "@id": "https://example.com/#localbusiness", "name": "...", "hasCredential": [{ "@type": "EducationalOccupationalCredential", "credentialCategory": "ИСО 9001" }], "areaServed": [ {"@type": "City", "name": "Санкт-Петербург"}, /* + 30 районов и городов ЛО */ ], "aggregateRating": { "ratingValue": "4.4", "reviewCount": "27" } }

Эффект: Rich Results Test чистый, schema централизована, при правках — один файл.

2. CLS 0.377 → 0.002 за день

Три раунда правок critical CSS в header.php:

  • .header-logo — flex-direction:column → flex-direction:row; align-items:center; gap:10px (минус 0.377)
  • .header-logo__icon — без размеров → width:48px; height:48px
  • .header-nav — отсутствовал в critical CSS → display:flex; align-items:center; gap:2px; flex:1 (минус 0.375 в этом раунде)

Inter Fallback на базе Arial с size-adjust:107%; ascent-override:90% — устранил FOUT-сдвиг.

3. Programmatic 100 страниц при 45%+ уникальности

Шаблон + уникальный $variable_content в каждой локации. Эффект: +44 страницы в индекс Яндекса за 11 дней (111 → 155), без doorway-риска.

4. Yandex Webmaster API recrawl + IndexNow + lastmod

Параллельно три механизма:

  • POST /recrawl/queue для 37 блог-статей в Excluded (квота 620 URL/сутки)
  • IndexNow ping → HTTP 202 от Bing и Yandex для 70 метро-URL
  • generate-sitemap.php пересоздаёт sitemap.xml с реальным lastmod из filemtime()

Эффект: 92 страницы из 93 Excluded вернулись в индекс за 13 дней.

5. Пассажи 134-167 слов с прямым ответом в первых 40-50

На 5 ключевых страницах. Эффект: средняя позиция кластера «уборка квартир + СПб + район» с 28.8 поднялась до 16-24, главные коммерческие страницы попадают в топ-15 при отсутствии ссылочного.

Инструменты, которые использовал

  • Google Search Console — главный источник правды по показам, кликам, средней позиции, индексации
  • Яндекс Вебмастер + Yandex Webmaster API — переобход, гео-привязка, анализ Searchable / Excluded
  • IndexNow — ping https://api.indexnow.org/indexnow с массивом URL, ключ в txt-файле в корне сайта
  • Yandex Wordstat API через Yandex Cloud — частотности 388 фраз за 8-10 минут, скрипт на Python
  • Screaming Frog — краулинг 474 URL, поиск тонкого контента, Near Duplicates для проверки уникальности программатика
  • Lighthouse CI — гонял после каждой пачки правок, цель: зелёная зона по всем метрикам
  • Snyk MCP — нашёл DOM XSS в main.js, XSS в indexnow.php, mixed content. Все исправлены
  • Playwright + Firecrawl — автоматизация аудитов на 200-500 URL
  • AmoCRM API JWT — контакты + сделки в воронке, без ручного ввода
  • Семантическое ядро в CSV — 861 фраза в 20 кластерах, отдельная колонка с Wordstat и долей кликов

Платных инструментов из списка — два: Screaming Frog (фриланс-лицензия) и Beget CDN (от 8 апреля). Остальное — бесплатное и нативное.

Сложности и грабли

  • Alias-домен на Beget. Папка с именем домена существует, но Nginx читает другую. Сутки потратил на отладку деплоя «в никуда»
  • OPcache на shared. PHP кэширует bytecode, после rsync изменения не видны 5-10 минут. Спас .user.ini с opcache.validate_timestamps=1; opcache.revalidate_freq=0, TTL применения до 300 секунд
  • Nginx whitelist. Beget генерирует Nginx-конфиг из .htaccess. Новые PHP-файлы возвращают 404. Workaround — RewriteRule с ?_inc=newmodule на существующий PHP-файл + require
  • CDN Beget переписывает Cache-Control. В .htaccess mod_expires ставит max-age=31536000, но Nginx выставляет 7 дней для CSS/JS, 30 дней для картинок, 5 минут для HTML. Не критично при versioned URL
  • Кэш cache.php 1 час. Обнаружил 22 апреля — после деплоя пользователи видели старый HTML. Фикс: rm -f cache/*.html после rsync
  • Дефолтный php в SSH = 5.6. Сайт на 8.4. Везде явно /usr/local/bin/php8.4 -l file.php, иначе линтер молча не парсит
  • Бэкдейтинг постов. Если опубликовать 11 статей одной датой задним числом, фильтры поисковиков считают это паттерном. Распределил по диапазону 20-30 апреля
  • /metro/ отдавал 410 + был в sitemap. Противоречие, Google путался. Убрал http_response_code(410) из metro.php, восстановил hub-страницу

Результаты

Сравнение «было — стало» за 50 дней:

  • GSC клики/мес: 52 → 97 (+86%)
  • GSC показы/мес: 2 826 → 5 359 (+90%), пиковые 595 в день 4 мая
  • GSC средняя позиция: 28.8 → 16-24 (по разным сегментам)
  • Я.Searchable: 111 (13 апреля) → 194-201 (5 мая)
  • Я.Excluded: 56 → 92 → 1 (с 93)
  • Я.Метрика SQI: ~30 → 40 (стабильно)
  • CLS: 0.377 → 0.002 → 0
  • LCP mobile: ~4.5 c → 2.4 c
  • TBT: ~ — → 60 мс (зелёная зона)
  • Lighthouse Performance mobile: ~60-70 → 93/100
  • Lighthouse SEO: — → 100/100
  • Lighthouse Accessibility: — → 97/100
  • SEO Health Score: 62 (5 апреля) → 72 (10 апреля) → 84 (23 апреля)
  • 5 «best of» упоминаний на профильных площадках в нише

Топ-запросы Яндекса по показам на 7 мая: «услуги клининга в СПб цены» — 103 показа, «уборка производственных помещений цена за 1 м²» — 79, «клининг стоимость» — 77, «клининг СПб» — 72. По кликам: брендовые в топ-3, плюс «уборка промышленных предприятий», «уборка подземных паркингов» (27 показов на позиции 6 после правки H2 и FAQ), «сколько стоит клининг 4-комнатной квартиры», «экспресс уборка Невский проспект».

Что дальше

Открытые задачи:

  • YouTube канал + 5 видео — корреляция с AI-цитированием 0.737, самый сильный сигнал
  • Wikidata-запись для компании
  • Reddit — 10-15 экспертных ответов в месяц
  • LinkedIn Company Page
  • 6 каталогов первой волны: Cleannow, Orgpage, Cataloxy, Rosfirm, Spr, Profi.ru
  • 1-2 публикации в деловых изданиях СПб → потом Wikipedia ru
  • llms-en.txt — английская версия для глобальных AI
  • Cloudflare free / Yandex Cloud CDN — для page-cache TTFB

Выводы

  1. Programmatic + блог-волна — рабочий джет на новый сайт в локальной нише. 100 уникальных шаблонных страниц + 50 длинных статей за месяц превращают новый домен из 0 в 200 страниц в Searchable Яндекса. Без покупных ссылок.
  2. Schema важна, но техника важнее. Один блок LocalBusiness с @id, правильный BreadcrumbList без якорей, отсутствие deprecated HowTo дают чистый Rich Results Test. CLS в зелёной зоне дороже всех schema вместе взятых, потому что Google и Яндекс ранжируют по Core Web Vitals.
  3. Возврат страниц из Excluded возможен и быстро. Yandex Webmaster API + IndexNow + актуальный lastmod в sitemap = 92 страницы вернулись из 93 за 13 дней. Не оставляйте Excluded гнить.
  4. AI-цитируемость закладывается параллельно с обычным SEO. llms.txt, AI-краулеры в robots.txt, пассажи 134-167 слов под H2, H2 в форму вопроса — это 7 правок, которые делаются за 2-3 рабочих дня и работают и для Google AIO, и для ChatGPT, и для Perplexity.
  5. Один человек тянет 245 URL. Без подрядчиков, без агентства, без покупных ссылок — реалистично, если есть прямой контроль над кодом и schema, дисциплина в темпе блога и инструменты для переобхода.

Если у вас похожая задача

Я веду это всё руками как руководитель отдела маркетинга в клининговой компании. На фрилансе предлагаю аналогичные форматы под другие ниши: