Как я получил 33 дубля страниц из-за одной строчки кода — и что с этим делать

Личный опыт установки Яндекс.Метрики в Next.js SPA и как одна опечатка чуть не похоронила позиции сайта в Яндексе

Я разрабатываю корпоративные сайты на Next.js. Недавно я столкнулся с проблемой, о которой нигде не пишут прямо: неправильная установка Яндекс.Метрики в SPA-приложении привела к тому, что сайт нашего клиента — Вольтикс — получил 33 дубля страниц в индексе Яндекса. Позиции просели. Дубли до сих пор не очистились полностью.

Расскажу что произошло, как это исправить и как не повторить мою ошибку.

Почему обычная установка Метрики не работает в SPA

В обычном сайте каждый переход на страницу — это полная перезагрузка браузера. Яндекс.Метрика сама фиксирует новый хит и знает, на какой странице находится пользователь.

В SPA (Single Page Application) — Next.js, Nuxt, React — страница не перезагружается. Меняется только URL в адресной строке и контент на экране. Метрика об этом не знает, если специально не сообщить ей вручную.

Результат без дополнительного кода: все визиты записываются как посещение одной страницы — главной. Вы не видите реальных данных о том, куда ходят пользователи.

Правильная структура подключения Метрики в Next.js

Создаём папку /components/Metrika/ и два файла внутри.

📁 ScriptMetrica.jsx — загрузка самого счётчика

jsx"use client"; import Script from "next/script"; export default function ScriptMetrica() { return ( <> <noscript> <div> <img src="https://mc.yandex.ru/watch/YOUR_COUNTER_ID - (ВАШ НОМЕР СЧЕТЧИКА)" style={{ position: "absolute", left: "-9999px" }} alt="" /> </div> </noscript> <Script id="metrika-counter" strategy="afterInteractive"> {`(function(m,e,t,r,i,k,a){ m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date(); for (var j = 0; j < document.scripts.length; j++) { if (document.scripts[j].src === r) { return; } } k=e.createElement(t),a=e.getElementsByTagName(t)[0]; k.async=1; k.src=r; a.parentNode.insertBefore(k,a); })(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); ym(YOUR_COUNTER_ID - (ВАШ НОМЕР СЧЕТЧИКА), "init", { defer: true, clickmap: false, trackLinks: false, accurateTrackBounce: false, webvisor: false });`} </Script> </> ); }

📁 Metrika.jsx — отслеживание переходов между страницами

"use client"; import { useEffect } from "react"; import { usePathname, useSearchParams } from "next/navigation"; export default function Metrika() { const pathname = usePathname(); const searchParams = useSearchParams(); useEffect(() => { // ✅ Правильно: добавляем ? только если параметры реально есть const url = searchParams.toString() ? `${pathname}?${searchParams}` : pathname; if (typeof window !== "undefined" && window.ym) { window.ym(YOUR_COUNTER_ID - (ВАШ НОМЕР СЧЕТЧИКА), "hit", url); } }, [pathname, searchParams]); return null; }

📁 Metrics.js — общая обёртка

import ScriptMetrica from "./components/Metrika/ScriptMetrica"; import { Suspense } from "react"; import Metrika from "./components/Metrika/Metrika"; export default function Metrics() { return ( <> <ScriptMetrica /> <Suspense fallback={null}> <Metrika /> </Suspense> </> ); }

Подключаем <Metrics /> в layout.js — и готово.

Скрин: структура папки в редакторе кода (VSCode) — три файла, как они вложены.
Скрин: структура папки в редакторе кода (VSCode) — три файла, как они вложены.

Одна строчка кода, которая стоила 33 дублей

Вот та самая ошибка. Я написал вот так:

// ❌ Неправильно const url = `${pathname}?${searchParams}`; ``` Казалось бы, мелочь. Но `searchParams.toString()` возвращает пустую строку `""` на страницах без параметров. В итоге каждый URL превращался в: ``` https://vlts.ru? https://vlts.ru/uslugi? https://vlts.ru/nashi-raboty? https://vlts.ru/kontakty? ``` Яндекс воспринял эти адреса как отдельные страницы — дубли реальных URL. Метрика в режиме переобхода исправно ходила по этим ссылкам и скармливала их индексу. **За один месяц — 33 дубля в индексе Яндекса.**

Что делать, если дубли уже появились

Шаг 1. Найти источник проблемы

Откройте Яндекс.Вебмастер → Индексирование → Страницы в поиске. Если увидите страницы по суффиксу `?` и раздел откуда счётчик метрики. Если видите URL вида `domain.ru/page?` — дубли пришли именно через Метрику.

Скрин:  Яндекс.Вебмастера, что будет с такими страницами.
Скрин: Яндекс.Вебмастера, что будет с такими страницами.

Шаг 2. Отключить переобход по счётчику

Яндекс.Метрика умеет обходить ссылки, которые встречает на сайте. Именно это усугубило ситуацию.

Идите в Яндекс.Вебмастер → Индексирование→ Обход по счётчикам. Отключите опцию «Переобход страниц по счётчику». Это остановит поток новых дублей.

Идите в Яндекс.Вебмастер → Индексирование→ Обход по счётчикам.
Идите в Яндекс.Вебмастер → Индексирование→ Обход по счётчикам.
Отключите опцию «Переобход страниц по счётчику
Отключите опцию «Переобход страниц по счётчику

Шаг 3. Исправить код

Замените проблемную строку на правильную — как показано выше. Задеплойте изменение.

const url = searchParams.toString() ? `${pathname}?${searchParams}` : pathname;

Шаг 4. Убрать дубли из индекса

После исправления кода дубли не уйдут сами сразу. Яндексу нужно время на переобход. Ускорить этот процесс не получится.

Также проверьте файл `robots.txt` — можно временно закрыть от индексации URLs с параметрами: Disallow: /*?

Но это крайняя мера. Основное — исправленный код и терпение.

Шаг 5. Следить за динамикой

Раздел «Страницы в поиске» в Вебмастере обновляется по мере того как Яндекс переобходит сайт. У нас дубли появились за месяц — уходят медленнее. Это нормально, но позиции страдают пока дубли живы.

Итого: чек-лист правильного подключения Метрики в Next.js

Счётчик загружается через strategy="afterInteractive" — не блокирует рендер

Ручная отправка хитов через ym("hit", url) при каждом изменении pathname

URL формируется с проверкой: searchParams.toString() ? ... : pathname

Компонент с useSearchParams обёрнут в <Suspense> — иначе Next.js упадёт при сборке

Переобход страниц по счётчику в настройках Метрики — отключён

Последствия, которые стоит знать заранее

Дубли страниц — это не просто некрасивые записи в Вебмастере. Яндекс распределяет «вес» страницы между оригиналом и дублем, что прямо влияет на позиции. Чем дольше дубли живут в индексе — тем глубже просадка.

Наш сайт получил заметное снижение позиций по нескольким коммерческим запросам именно в тот период. Часть позиций восстановилась после исправления кода, часть — ещё в процессе.

Вывод простой: в SPA метрику нельзя ставить «как обычно». Нужен дополнительный код для отслеживания переходов — и важно проверить, что URL формируется без лишних символов.

11