Міграція на SPA коштувала -25% органіки. Як ми повернули зростання - і навіщо сайту «реліз-гейт»
Міграція сайту на SPA - це той випадок, коли рішення ухвалюють розробники, а наслідки першим бачить маркетолог: у звіті з органічного трафіку. Розповідаємо кейс нашого клієнта - національної мережі ремонту турбокомпресорів з інтернет-магазином на ~3000 товарів. У кейсі дві ітерації фронтенду: перша - SPA на JavaScript із самописним фреймворком, після якої органіка впала; друга - переписування на React із SSR, яке пройшло через реліз-гейт. Сюжет чесний, без «переїхали ідеально» і без «усе пропало»: просадка й зупинка зростання при втриманих топах, діагностика, програма виправлень, реліз через формальні критерії приймання - і повернення до росту.
Одразу термінологія, щоб далі говорити однією мовою. SPA (Single Page Application) - застосунок, де браузер отримує майже порожній HTML, а весь вміст домальовує JavaScript. SSR (Server-Side Rendering) - підхід, за якого сервер віддає вже готовий HTML з текстом, заголовками й посиланнями, а JavaScript лише «оживлює» сторінку. Для користувача різниця непомітна. Для пошукового бота - критична.
Акт 1. Контекст: стабільна органіка і бізнес-рішення про переїзд
Клієнт - мережа з філіями по Україні: ремонт турбокомпресорів плюс каталог запчастин на ~3000 SKU, двомовний сайт. Органіка стабільна: за SimilarWeb - близько 6 941 десктоп-візитів на місяць. Двигун каналу - категорії й картки товарів: для e-commerce саме вони збирають комерційний попит (як це працює системно - розбирали в гайді про SEO для інтернет-магазинів.
Рішення про переїзд зі старого PHP-сайту на сучасний JavaScript-стек ухвалили з міркувань продукту: швидший інтерфейс, зручність розробки, редизайн. Перша ітерація виглядала так: бекенд - Node.js, фронтенд - SPA на JavaScript із власним, самописним фреймворком. Класична ситуація, яку ми бачимо регулярно: стек обрали з погляду розробки - а SEO-наслідки не прораховував ніхто.
Акт 2. Просадка: -25% трафіку і зупинка зростання
Нову SPA-версію задеплоїли [дата деплою, 01-02.2026]. Одразу після переїзду органічний десктоп-трафік просів на 25%: з ~6 941 до 5 197 візитів на місяць.
Перше, що хочеться сказати в такій ситуації, - «ринок просів». Ми перевірили. Ринок і справді був неспокійний, але неоднорідний:
Сайт | Динаміка 02.2026 |
|---|---|
Клієнт | -25% |
Конкурент №1 | -52% |
Конкурент №2 | +26% |
Один гравець втратив половину, інший - виріс на чверть. Списати все на сезон чи ринок не вийшло. Причину треба було шукати в самому сайті.
І тут важлива чесна деталь, яка відрізняє цей кейс від історій про катастрофу. Сайт не обвалився: за ключовими запитами він загалом утримався в топах. Але він перестав рости - при живому попиті й активному ринку. Для власника така картина часто виглядає як «усе гаразд, просто період такий». Для нас стагнація на тлі збереженого попиту - симптом нездоров’я. Саме вона, а не самі лише -25%, стала сигналом іти в глибокий аудит.
Акт 3. Діагностика: що насправді побачив Googlebot
Тут почалася найважливіша частина кейсу. Ми зняли сторінки за протоколом «сирий HTML без виконання JavaScript» - тобто подивилися на сайт очима бота в момент першого запиту (окрема причина робити це власним протоколом: датацентрові запити сайт відсікав на рівні Cloudflare, тож питання «звідки бачить бот» вимагало окремої перевірки).
Одразу розставимо акценти, бо тут легко перекрутити. Googlebot не був «сліпим»: контент на сайті існував, і після рендерингу JavaScript бот його отримував. Проблема в іншому - контенту не було в HTML. Він жив у JavaScript-контейнері, і цей контейнер тягнув на кожну сторінку майже всю структуру сайту. Первинний аудит звівся до чотирьох знахідок:
- Немає HTML. У сирому HTML - нуль контенту: весь текст, посилання й заголовки сиділи всередині JS-контейнера.
- Бот отримував дані не з першого заходу. Перший запит віддавав порожній каркас; справжній вміст Google бачив лише після рендерингу JavaScript - з черги, яка живе своїм розкладом.
- Великий розмір сторінок. До 1 МБ HTML на сторінку при нормі <150 КБ.
- Відкриті дані для парсингу. JSON-контейнер з контентом і майже повною структурою сайту лежав відкритим на кожній сторінці - будь-який парсер міг зняти каталог і всі SEO-тексти одним запитом.
У цифрах перший запит бота виглядав так:
Що перевіряли (сирий HTML) | Факт після переїзду (аудит 03.2026) |
|---|---|
Посилання <a href> у <body> поза <script> | 0 (із 346 на головній для користувача) |
Теги H1 | 0 |
Текст поза скриптами | 0 символів |
Зображення | 0 |
Вага HTML сторінки | до 1 МБ при нормі <150 КБ; /site-map - 5,5 МБ |
Інлайн-payload | 727 КБ в одному скрипті |
Окрема деталь, яку люблять розробники: змінна з усім цим станом називалася ssrdata - але жодного Server-Side Rendering вона не робила.
Масштаб контейнера найкраще показує сторінка послуг: 303 теги H2, з яких по темі - лише 6. Решта - SEO-тексти інших сторінок, що «їхали» в payload меню на кожну сторінку двома мовами (~90 КБ чужого контенту, на сторінці контактів - 26 повних текстів інших розділів). 43% JSON-payload - внутрішні дублі; власний контент сторінки - 2,9%.
Чому це гальмує ріст - механіка простими словами. Google обробляє JavaScript-сайти у фази: сканування сирого HTML відбувається одразу, а рендеринг JavaScript стоїть в окремій черзі, яка може розтягуватися (Google Search Central: Understand JavaScript SEO Basics).
Ключова інженерна деталь: нові URL Google відкриває через посилання, а посилання бот знаходить лише в елементах <a> з атрибутом href. Немає їх у сирому HTML - краулер не може вчасно будувати граф зв’язків для нових і оновлених сторінок: це удар одразу по discovery і краулінговому бюджету. Старі сторінки ранжуються за збереженим історичним індексом - тому топи трималися. А нові посадкові, оновлені картки і свіжі тексти підхоплюються «другим темпом» і зависають «сиротами» - тому росту не було: все нове бот бачив пізно і дорого.
Для фахівців - врізка: у типових провальних міграціях на SPA втрати сягають -60…80% органіки. Тут просадка зупинилася на -25%, і це не везіння. При переїзді команда зберегла 100% URL-структури (нуль 404), коректні hreflang, canonical і Schema.org. Це «міграційна гігієна» - саме вона втримала позиції в топах, поки рендеринг-борг з’їдав потенціал росту. Як ми знаходимо такі речі системно - окремий матеріал: кейс технічного SEO-аудиту.
Акт 4. Програма дій: два паралельні треки
Виправлення розклали на два треки, які йшли одночасно.
Трек А - технічний. Вимоги до розробників: впровадити SSR (H1, текст, меню, посилання на товари й зображення - у сирому HTML до виконання JS); «payload-дієта» - вичистити з пейлоада чужі SEO-тексти, дублікати й невикористовувану мовну версію з планом 727 КБ → 60-90 КБ; виправити soft-404, роздуті title, пагінацію; додати lazy loading і комерційну Schema.org (ItemList / Product / AggregateRating). Команда розробки вирішила закривати ці вимоги другою ітерацією - переписуванням фронтенду з самописного фреймворку на React із SSR.
Трек Б - контентно-структурний. Поки розробники будували SSR, працювали з тим, що не залежить від релізу: нові посадкові під вузькі послуги (актуатор, діагностика) й регіональні сторінки; переоптимізація сторінок під реальний інтент - показовий випадок: сторінка була заточена під «очистку» DPF, а частотний запит ринку - «промивка»; системна робота з відгуками в Google Business Profile.
Local SEO дало найнаочніший урок кейсу. Філія в одному з обласних центрів тримає Local Pack #1 з рейтингом 4.9 і 143 відгуками - проти конкурента з 839 відгуками, але рейтингом 4.3. Якість відгуків важить більше за кількість.
Акт 5. Реліз-гейт: чому ми не пропустили реліз розробників
Коли розробники представили другу ітерацію - фронтенд, переписаний на React із SSR, - її можна було просто випустити. Ми не випустили.
Аудит прототипу за тим самим протоколом сирого HTML показав 9 регресів проти 5 покращень: 50 битих зображень, H1 головної «прибитий» до першого слайда рекламного банера, canonical на чужий хост, відсутній sitemap, конфліктний robots.txt. Другого падіння трафіку бізнес міг і не пережити.
Розробники поспішали. Ми - ні.
Прототип повернули на доопрацювання - не з коментарем «щось не так», а з формальними критеріями приймання, які перевіряються на сирому HTML без виконання JS:
Критерій приймання релізу | Поріг |
|---|---|
Посилання меню й товарів як <a href> | ≥30 у сирому HTML |
H1 | 1, релевантний сторінці |
Текст поза скриптами | ≥1500 символів |
Вага сторінки | <150 КБ |
Биті зображення | 0 |
Sitemap | валідний |
Canonical | self-referencing |
Це і є «реліз-гейт»: страховка, яка коштує один аудит - і завжди дешевша за друге падіння трафіку. Саме тому ми вважаємо, що SEO-вимоги мають сидіти в ТЗ на розробку сайту з першого дня, а не з’являтися після релізу.
Акт 6. Реліз і цифри «до → після»
React-версію з SSR викатили на прод 22.08.2026. Контрольний аудит за критеріями приймання: 7 із 9 виконано повністю. Ключові виміри:
Метрика | Після переїзду (03.2026) | Після SSR-релізу (22.08.2026) |
|---|---|---|
Вага HTML головної | 978-1210 КБ | 108 КБ (-91%) |
<a href> у сирому HTML головної | 0 | 179 |
H1 у сирому HTML | 0 | 1 на кожній сторінці |
Текст поза скриптами (головна) | 0 символів | 11 093 |
Інлайн-payload | 727 КБ | 32 КБ (-96%) |
H2 на сторінці «Ремонт турбін» | 303 | 10 |
Чужі SEO-тексти на сторінках | ~90 КБ | 0 |
Сторінка /site-map | 5 505-5 687 КБ | 136 КБ (-98%) |
Неіснуючий URL | 200 (soft-404) | 404 |
Title пагінації | дублі на 148 сторінках | унікальні («Сторінка №2»…) |
Schema категорій | Article (нерелевантна) | ItemList + Product + Offer + BreadcrumbList + FAQPage |
Schema товару | базова | Product + Brand + Offer + AggregateRating + FAQPage |
Биті зображення (головна/категорія) | 18 / 32 (на прототипі) | 0 / 0 |
loading=”lazy” | 0 із 63 зображень | 91 із 94 (hero - eager + fetchpriority=high) |
TTFB / повне завантаження головної | FCP 1,7 с «на межі» | 206 мс / 2,0 с |
Sitemap-інфраструктура | частково | 2 індекси × 15 карт + карта зображень (10,5 тис. URL) |
А тепер - найважливіше про чесність цифр. Зростання почалося ще до SSR-релізу: за GA4 клієнта, organic + direct додали +20% за 05-08.2026; незалежний зріз SimilarWeb від 12.08.2026 - 37,3 тис. візитів за 3 місяці, +16,8% за останній місяць, органіка - топ-джерело з часткою 36%. Це результат контентно-структурного треку Б - не SSR. Ефект самого SSR-релізу на індексацію й позиції фіксується у Search Console окремо, через 4-6 тижнів після релізу. Ми свідомо не змішуємо ці два ефекти: методологія дала результат ще до головного технічного фікса - і це сильніший аргумент, ніж «SSR усе врятував».
Що ще не закрито - говоримо прямо
Кейс не закінчений, і ми не вдаватимемо, що все ідеально. У беклозі після релізу: H1 головної досі прив’язаний до першого слайда банера (хотфікс №1); окремі важкі каталожні шаблони досі важать 210-278 КБ при цілі <150 КБ - головна вже 108 КБ, але не всі типи сторінок дотяглися до неї (і це вже точно не 1 МБ); довгі title карток товарів (140-156 символів - було 447); alt-атрибути відсутні у ~65% зображень; паралельні namespace /catalog/* і /category/*; одне внутрішнє посилання на 404 зі сторінки послуг. Це нормальний стан живого проєкту: реліз закрив критичне, далі - планова робота.
Як ми ведемо міграції в ADS group
Міграція - це завжди дві команди в одній зв’язці. У нас це влаштовано так: Dev-Unit будує застосунок, SEO-Unit формулює вимоги до сирого HTML ще на етапі ТЗ, веде діагностику за власним аудит-протоколом - і має право не пропустити реліз, поки критерії приймання не виконані. Саме ця конструкція в кейсі спрацювала двічі: спершу знайшла справжню причину падіння, потім зупинила реліз з 9 регресами. Якщо вам належить переїзд на новий фронтенд - правильний момент прийти на технічний SEO-аудит - до релізу, а не після падіння.
Чек-лист: міграція на SPA без SEO-катастрофи
До міграції: зафіксувати базлайн (трафік, позиції, повний список URL); внести SSR або пре-рендеринг у ТЗ підряднику - «потім докрутимо» не працює; зберегти 100% URL або підготувати мапу 301.
Критерії приймання релізу - перевіряються на сирому HTML, без виконання JS: посилання меню й товарів як <a href>; один релевантний H1; основний текст присутній; вага сторінки <150 КБ. Canonical - тільки self-referencing. Sitemap - валідний, без сміттєвих URL. 404 віддає 404, а не soft-200. Нуль битих зображень, lazy loading на всьому, крім hero.
Після релізу: зняти стейджинг-noindex за регламентом; прогнати ключові шаблони через GSC Live Test; поставити annotation дати релізу в аналітиці; моніторити статистику сканування (розмір відповіді має впасти, кількість сканувань - зрости); контрольний зріз позицій через 4-6 тижнів.
Окремо: WAF/Cloudflare може різати краулерів незалежно від robots.txt - виняток для Verified Bots обов’язковий.
Це критерії саме для SPA-переїзду. Повна процедура будь-якої міграції - домен, CMS, структура URL, карта редиректів, staging - у нашому окремому матеріалі: чек-лист міграції сайту.
Міграція на SPA не вирок для органіки - вирок, коли сирий HTML порожній, а релізи виходять без критеріїв приймання. Фреймворк тут ні до чого: у цьому кейсі падіння дав самописний JavaScript-фронтенд без SSR, а відновлення закріпила React-версія, яку пропустили на прод тільки через реліз-гейт. Бізнес втратив 25% органіки через те, що SEO покликали після деплою, - і зростання стартувало ще до головного технічного фікса, коли з’явилися діагностика і два треки робіт.
Якщо ви плануєте редизайн чи зміну фронтенду - порахуйте вартість аудиту до міграції та порівняйте з ціною кількох місяців просілої органіки. Замовте аудит перед міграцією або розробку, де SEO-вимоги закладені в ТЗ - обидва шляхи дешевші за відновлення після падіння.
Часті питання
Чи шкодить React SEO?
Ні - шкодить будь-який фронтенд, що віддає порожній сирий HTML. У нашому кейсі падіння спричинив самописний JavaScript-фреймворк без SSR, а React-версія з SSR якраз закріпила відновлення. Фреймворк - самописний, React чи Vue - вторинний; первинне те, що бот бачить у HTML до виконання скриптів.
Скільки Google рендерить JavaScript?
Сканування сирого HTML відбувається одразу, а рендеринг JS стоїть в окремій черзі: Google документує, що сторінка може чекати секунди, але може й довше. Для каталогу на тисячі SKU ця затримка множиться на кожну сторінку - тому контент у сирому HTML надійніший.
У нас уже впав трафік після редизайну. Що робити спершу?
Зняти сторінки в сирому HTML без виконання JS і порівняти з тим, що бачить користувач: посилання, H1, текст, вага. Якщо бот бачить порожнечу - причина знайдена, і вона лагодиться. Паралельно перевірити, чи збереглися URL, canonical і hreflang.
Коли повернеться трафік після виправлень?
Google строків переіндексації не документує, тому чесна відповідь - залежить від масштабу сайту й глибини проблеми. У цьому кейсі контентний трек дав +20% за три місяці ще до SSR-релізу; ефект самого SSR оцінюється через 4-6 тижнів після релізу за GSC.

Час вийшов



