Маркетинг-архитектура · 25 мин

Как уйти с Тильды на Astro и не обвалить SEO

Методика двухфазной миграции с Tilda на Astro: пиксельная копия → постепенная чистка. 6 этапов, скриншот-тестирование, без обвала органики и потери back-links.

Картонная коробка-переезд с лаймовой стропой катится от старого тёмного фасада к новому минималистичному зданию

Представьте: у вас сайт на Тильде. Несколько лет туда стекался трафик, накопились отзывы и ссылки с других сайтов, в Яндексе и Google вы стоите в выдаче по нужным запросам. Вроде всё работает. Но вы видите, что скорость сайта стабильно красная, новых интеграций (CRM, AI-ассистент, нормальный калькулятор) нормально не поставить, а подписка стала дороже, чем своё хостинговое решение. И вы знаете, что переезжать пора. И боитесь.

Боитесь — правильно. Главная цена за неправильную миграцию — это месяцы потерянного SEO. Ссылки уходят в никуда, поисковики «забывают» страницы, накопленный годами органический трафик обнуляется. Я видел это у нескольких клиентов до того, как они пришли — и теперь мы переезжаем правильно.

Эта статья — про методику, по которой я сейчас перевожу с Тильды на Astro нашу стоматологию Зубчик.ру. Проект ещё в процессе, цифр «до/после» я опубликую позже. Но методика уже отработана, проверена на 31 странице и 590 картинках, и я хочу её показать целиком — потому что таких разборов на русском почти нет, а вопросов про «как уйти с Тильды и не обвалить трафик» с каждым месяцем больше.

Когда Тильда — это нормальный выбор

Сразу зафиксирую честную позицию. Тильда — отличная платформа под определённый класс задач. Я не буду писать «фу, конструктор, надо на код». Это лень и снобизм. Тильда экономит сотни тысяч рублей на разработке там, где разработка не нужна.

Тильда хорошо подходит, если:

  • У вас ИП или маленький бизнес, лендинг под одну услугу или один филиал — тут платить за разработку дороже, чем за пару лет подписки.
  • У вас MVP — нужно проверить идею, собрать первые продажи, понять, есть ли вообще спрос. Тильда позволяет за выходные собрать что-то живое.
  • У вас B2C-услуга, где сайт — это «визитка с формой записи». Стоматология одного врача, маникюрный салон, частная школа танцев. Конверсии решает оффер и реклама, не платформа.
  • У вас ивент или акция: лендинг живёт три месяца и умирает. Перебор — заказывать кастом.

Я сам активно работаю с Тильдой у нескольких клиентов и не собираюсь на этом ставить крест. Знаю её до уровня кастомного кода в T123, разметки Schema.org через HTML-вставки, кастомизации корзины через Store API. Если вам хорошо на Тильде — оставайтесь. Эта статья не для вас.

Она для тех, кто уже упёрся в потолок — но боится сделать следующий шаг.

Пять признаков, что вы упёрлись в потолок

По моему опыту, сигналы примерно такие.

1. SEO не растёт, несмотря на контент. Вы пишете статьи, обновляете услуги, делаете всё «как надо», но органика стоит на месте или медленно ползёт вниз. Проверяете в Яндекс.Метрике / Search Console [1] — конкуренты на тех же запросах обходят вас, хотя у вас вроде контент лучше. Часто причина — техническая: тяжёлая Тильда-страница плюс отсутствие нормальной Schema.org разметки [8] — и поисковики ставят вас ниже.

2. Core Web Vitals красные и не лечатся. Вы открываете Lighthouse или PageSpeed Insights [2], видите Performance 30–40, LCP 4 секунды, INP 350 мс. Пытаетесь оптимизировать — закрываете лишние блоки, режете картинки. Лучше не становится: Тильда грузит свои бандлы CSS и JS целиком, jQuery, плюс картинки в hero подгружаются лениво [7], что для главной — плохая идея с точки зрения LCP [4]. Это архитектурная проблема платформы, её не починить настройками.

3. Хочется нестандартных интеграций — и это становится больно. Подключить AI-ассистента, нормальный калькулятор стоимости, кастомную CRM-форму, A/B-тесты без костылей. На Тильде всё это либо невозможно, либо собирается через T123 + jQuery — каждое такое решение хрупкое и при следующем обновлении платформы может отвалиться.

4. Сайт стал большим, и его поддерживать тяжело. 30+ страниц услуг, блог на 100+ статей, отдельные посадочные. Каждое глобальное изменение (например, новый филиал → надо обновить везде) превращается в ручную правку десятков страниц. Шаблонизации в нормальном смысле этого слова на Тильде нет.

5. Подписка стала дороже своего хостинга. Бизнес-тариф Тильды на 2026 — это условные 1500 ₽ в месяц [10]. 18 000 ₽ в год. Своё решение на Astro + статичный хостинг + автодеплой — 0–500 ₽ в месяц. Окупаемость миграции по чистой подписке — год-полтора, а к ней ещё прибавляется выигрыш в SEO и скорости.

Если узнаёте у себя три из пяти — значит пора. Дальше вопрос как.

Главный страх миграции — обвал SEO

Большинство клиентов, которые откладывают переезд, откладывают его не из-за денег. Откладывают из-за страха, что после миграции обнулится трафик.

И страх обоснованный. Я видел кейсы, где после неаккуратного переезда сайт терял 30–50% органического трафика и не восстанавливался год. Причины всегда одни и те же:

  • URL поменялись, а 301-редиректов не сделали — все накопленные ссылки и позиции в выдаче ушли в никуда.
  • Сделали редиректы, но «как получится»: одна посадочная склеилась с другой, потерялась глубина структуры, поисковик увидел потерю контента и понизил весь домен.
  • Добавили цепочку редиректов в 4–5 хопов («tilda.cc → www.site.ru → site.ru → /new-url») — Google такие цепочки штрафует [3], потому что они тратят его краулинг-бюджет.
  • Запустили новый сайт, но сразу удалили старый. Поисковик ещё не обошёл редиректы — а уже видит 404 и начинает деиндексировать страницы.
  • На новом сайте поломали разметку (нет canonical, нет Schema.org, кривые <title>), и Google решил, что новый сайт — некачественный.

Параллельно работает скоростной фактор [2]: после June 2021 Page Experience Update от Google, Core Web Vitals — официальный сигнал ранжирования. Если новый сайт быстрее старого — вы получаете SEO-буст. Если медленнее или такой же — теряете часть позиций уже на старте.

Хорошая новость: всего этого можно избежать. Тильда → Astro переходится без обвала, если делать руками и в правильном порядке. Об этом дальше.

Идея решения — двухфазная миграция

Главная ошибка миграции, которую делают почти все: «раз уж всё равно переезжаем, давайте заодно и редизайн сделаем». Меняют платформу, дизайн, тексты, структуру URL — всё одновременно. И тогда уже невозможно понять, что именно сломалось: то ли тексты, то ли скорость, то ли структура, то ли вёрстка. И починить нельзя — слишком много переменных меняется одновременно.

Метафора. Представьте, что вы переезжаете в новую квартиру. И решаете заодно сделать ремонт, поменять мебель, перебрать вещи и завести кота. Через месяц жить в этой квартире невозможно. Кота не нашли, мебель не приехала, ремонт не доделан, коробки стоят посреди комнаты.

Правильно — сначала переехать. Поставить вещи туда, где они стояли в старой квартире, повесить полки на те же места, кошку оставить в той же корзинке. Сначала жить как раньше, но в новом месте. И только потом, постепенно, делать ремонт, обновлять мебель, разбирать коробки. По одной коробке. С контролем, что не разбилось.

Так же делается миграция сайта. Двумя фазами:

  • Mode A — пиксельная копия. Берём Тильда-вёрстку как есть. Копируем HTML, CSS и картинки в Astro-проект. Накладываем сверху правильную SEO-обвязку (canonical, Schema.org, структурированные данные). Цель — чтобы новые страницы выглядели визуально один-в-один с Тильдой и работали по тем же URL. Поисковик в этот момент не видит никаких изменений в контенте — только техническое улучшение скорости.
  • Mode B — постепенный рефакторинг. Когда Mode A сдан и сайт живёт на Astro в продакшне, можно поблочно переписывать вёрстку на собственные Astro-компоненты, выкидывать тяжёлый Тильда-CSS, убирать jQuery. По одному блоку за раз, с контролем, что визуально ничего не изменилось. Растягивается на месяцы — и это нормально. У клиента уже работающий быстрый сайт, давление спадает.

Эта двухфазность — не моя выдумка, она проверена на больших миграциях в больших компаниях. Но в русскоязычной нише про неё говорят редко: чаще предлагают «переписать с нуля и в полтора раза быстрее» — что обычно заканчивается плохо.

Шесть этапов, на практике

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

Этап 0. Дискавери — никакого кода

Цель — понять, что именно мы переносим. Не написав ни строчки кода.

Что делаем:

  • Скачиваем Тильда-выгрузку (Настройки → Экспорт → ZIP).
  • Строим карту страниц: для каждой страницы фиксируем URL, заголовок, файл в выгрузке, какие подключаются CSS- и JS-бандлы.
  • Снимаем скриншоты-эталоны каждой страницы. Это критично — позже скриншоты будут единственным объективным критерием «совпало / не совпало».
  • Отдельно фиксируем шапку, подвал, 404-страницу — Тильда хранит их в отдельных файлах.
  • Выписываем все нестандартные блоки: кастомный код в T123, формы, попапы, корзину.

Карта URL → файл → title строится одной командой по выгрузке. Положите этот скрипт рядом с распакованным архивом и запустите:

cd tilda-archive
for f in page*.html; do
  url=$(grep -oE 'rel="canonical" href="https://[^"]+"' "$f" \
         | sed 's|.*://[^/]*||;s|"||')
  title=$(grep -oE '<title>[^<]+</title>' "$f" | head -1 \
         | sed 's|<title>||;s|</title>||')
  echo "$f | $url | $title"
done | sort -t'|' -k2 > ../CONTENT-MAP.md

На выходе получаете готовый список «файл → URL → заголовок» — основа для будущей карты страниц нового сайта.

Эталонные скриншоты снимаются через headless Chrome без всяких сторонних сервисов:

# Поднимаем оригинал Тильды локально на 8998 порту
cd tilda-archive
python3 -m http.server 8998 --bind 127.0.0.1 &

# Снимаем скриншоты ключевых страниц
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
mkdir -p _screenshots

for page in page69726289 page70003977 page70174449; do
  "$CHROME" --headless=new --disable-gpu --no-sandbox \
    --window-size=1280,8000 --hide-scrollbars \
    --virtual-time-budget=30000 \
    --run-all-compositor-stages-before-draw \
    --screenshot=_screenshots/$page-tilda.png \
    "http://127.0.0.1:8998/$page.html"
done

Окно 1280×8000 нужно, чтобы захватить весь скролл сайта целиком, а не только первый экран. --virtual-time-budget=30000 даёт Тильде 30 секунд на ленивую загрузку картинок и анимации — на меньшем значении скриншот выходит наполовину пустой.

Критерий приёмки: есть таблица «URL → файл → ассеты» на каждую страницу и папка скриншотов-эталонов.

Этап 1. Каркас Astro и перенос ассетов

Создаём пустой Astro-проект. Подключаем sitemap-интеграцию и MDX. Картинки, CSS и JS из Тильда-выгрузки кладём в public/tilda/не переименовывая относительные пути, потому что в Тильда-HTML эти пути захардкожены.

npm create astro@latest my-site -- --template minimal --no-install --no-git
cd my-site
npm install
npm install @astrojs/sitemap @astrojs/mdx

# Переносим ассеты Тильды
mkdir -p public/tilda
cp -r ../tilda-archive/{css,js,images,files} public/tilda/

# Корневые
cp ../tilda-archive/robots.txt public/ 2>/dev/null
cp ../tilda-archive/favicon.ico public/ 2>/dev/null

Префикс /tilda/ важен — без него картинки и CSS Тильды затирают потом ваши собственные ассеты, когда вы начнёте их добавлять.

Здесь главная техническая тонкость — Тильда хитро лениво грузит картинки через свой lazyload-1.3.min.js: реальный путь к картинке лежит в атрибуте data-original, а не в src. Если просто скопировать HTML, картинок не будет. Поэтому отдельным шагом переписываем все эти пути на новые префиксы — три regex-замены поверх HTML:

// 1) css/, js/, images/, files/ в src=, href=, url(...)
html = html.replace(/(["'(])(css|js|images|files)\//g, '$1/tilda/$2/');

// 2) data-original="images/..." (lazy-load) — отдельно
html = html.replace(
  /data-original=(["'])(?!https?:|\/)([^"']+)/g,
  (_m, q, val) => `data-original=${q}/tilda/${val}`
);

// 3) url(...) внутри inline <style>
html = html.replace(/url\(\s*(['"]?)(css|js|images|files)\//g, 'url($1/tilda/$2/');

Эти замены делаются на сервере при сборке Astro, не на клиенте — к моменту, когда HTML дойдёт до браузера, все пути уже корректные.

Критерий приёмки: dev-сервер поднимается, ассеты Тильды отдаются по новым путям, страницы пока пустые.

Этап 2. Перенос страниц 1-в-1

Создаём в Astro единый универсальный роутер, который для каждого URL загружает соответствующий Тильда-HTML и рендерит его через специальный layout. Никаких новых компонентов. Просто читаем HTML из выгрузки и вставляем в страницу.

И тут вторая критичная ловушка. У Тильды каждый блок (слайдер отзывов, FAQ-аккордеон, Яндекс.Карта) инициализируется вот таким <script> прямо в теле страницы:

<div id="rec1060129486">...</div>
<script>t_onReady(function() { t_sldsInit('1060129486', {}); });</script>

Если просто вставить этот HTML в Astro через стандартный механизм — скрипт попадёт в DOM, но не выполнится. Это стандартная защита браузеров от XSS через innerHTML. Без выполненных скриптов слайдеры пустые, FAQ не открывается, карта не отрисована. Я на этом провёл несколько часов, прежде чем понял, в чём дело.

Решение — на сервере, при загрузке Тильда-HTML, вытащить все <script> отдельно и потом отрендерить их в Astro как настоящие теги. Тогда браузер их выполняет:

---
// src/layouts/TildaLayout.astro (упрощённо)
const { page } = Astro.props;
---
<html lang="ru">
  <head>
    <!-- ваш SEO-блок -->
    {page.cssLinks.map((href) => <link rel="stylesheet" href={href} />)}
    {page.inlineStyles.map((css) => <style set:html={css} is:inline />)}
  </head>
  <body>
    <Fragment set:html={page.body} />
    {page.bodyInlineScripts.map((code) => (
      <script set:html={code} is:inline />
    ))}
  </body>
</html>

Ключевое — <script> создаются Astro-рендером, а не вставляются через set:html в общий блок. Браузер такие <script> выполняет нормально.

Критерий приёмки: каждая страница Astro визуально совпадает со скриншотом Тильды на 98% и выше. Если меньше — ищем, что не загрузилось (обычно либо CSS-бандл, либо init-скрипт, либо шрифт).

Этап 3. SEO-обвязка

Самое интересное. Тильда даёт минимальный SEO: title, description, og:image. Что она не даёт:

  • Структурированные данные JSON-LD [8] — критично для медицины (MedicalProcedure, Dentist, LocalBusiness), важно для услуг, обязательно для FAQ-блоков.
  • Контроль HTTP-заголовков (HSTS, Cache-Control).
  • Удобный механизм управления хлебными крошками для всего сайта.
  • Twitter Cards.
  • Полный контроль над canonical при переезде домена.

В Astro мы заводим один файл — карту страниц со всеми SEO-полями. Для каждой страницы прописываем: title, description, ключевые слова, тип (услуга/блог/контакты), хлебные крошки, FAQ. Layout автоматически рендерит всю обвязку в <head> и нужные JSON-LD блоки. Контент Тильды при этом не трогается — вёрстка та же.

// src/data/page-map.ts
export const PAGES = [
  {
    url: '/uslugi-i-tseny/implantatsiya-zubov',
    file: 'page70003977.html',
    type: 'service',
    title: 'Имплантация зубов от 44 000 ₽ — Зубчик.ру',
    description: 'Имплантация под ключ за один визит, импланты Osstem и Straumann, гарантия 10 лет.',
    keywords: ['имплантация москва', 'osstem', 'straumann'],
    breadcrumbs: [
      { name: 'Главная', url: '/' },
      { name: 'Услуги и цены', url: '/uslugi-i-tseny' },
      { name: 'Имплантация зубов' },
    ],
    procedure: {
      name: 'Имплантация зубов',
      procedureType: 'http://schema.org/SurgicalProcedure',
      priceFrom: 44000,
    },
    faq: [{ q: 'Сколько времени занимает имплантация?', a: '...' }],
  },
];

Один файл — вся SEO-карта сайта. На него потом можно посадить копирайтера: правит только этот документ, без знания Astro/HTML.

Минимальный универсальный JSON-LD компонент для FAQ-блоков:

---
// src/components/schema/FAQSchema.astro
const { items } = Astro.props;
const schema = {
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: items.map((i) => ({
    '@type': 'Question',
    name: i.q,
    acceptedAnswer: { '@type': 'Answer', text: i.a },
  })),
};
---
<script type="application/ld+json" set:html={JSON.stringify(schema)} is:inline />

is:inline обязательный — без него Astro попытается обработать содержимое как ES-модуль и сломает разметку. Тот же паттерн работает для MedicalProcedure, BreadcrumbList, Service, Article.

Критерий приёмки: валидатор Schema.org [8] показывает 0 ошибок, Lighthouse SEO = 100. Скриншоты не изменились (мы добавили только то, что не видно глазу).

Этапы 4–5. Переключение и мониторинг

Когда этапы 0–3 сданы, делаем staging-копию на временном домене, прогоняем через PageSpeed, валидаторы Schema.org, проверяем sitemap. После этого настраиваем 301-редиректы и переключаем DNS на новый сайт.

Дальше 2–4 недели смотрим в Search Console и Метрику. Если позиции по ключевым запросам в первые 2 недели поплыли вниз — это нормально (поисковики ходят и переиндексируют). Через 3–4 недели должны восстановиться плюс получить прирост от скорости.

Критерий приёмки: трафик в Метрике/Search Console не упал ниже 90% от старого через 4 недели. Если упал — копаем, что отвалилось.

Этап 6. Mode B — постепенный рефакторинг

Сюда переходим, когда сайт уже живёт в проде и стабилен. По одному блоку за раз: выносим шапку и подвал в Astro-компоненты (это сразу экономит 30–50 КБ на каждой странице), переписываем FAQ на нативные <details> вместо тильдовского аккордеона, мигрируем критичные картинки на встроенный механизм оптимизации Astro (он автоматически делает WebP/AVIF и responsive srcset [7]).

Каждое изменение — со скриншот-проверкой. Сравниваем «было / стало» через ImageMagick: разница больше 1% — откатываем, разбираем, что сломалось.

Это месяцы работы, не дни. И это нормально — все эти месяцы у клиента уже быстрый сайт в проде.

Скриншот-тестирование как дисциплина

Это, наверное, самая важная часть всей методики. Один абзац, на который стоит обратить внимание.

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

Поэтому единственный объективный критерий — попиксельное сравнение скриншота Astro со скриншотом-эталоном Тильды. Берём headless Chrome, рендерим страницу 1280×900 (а лучше 1280×8000, чтобы захватить весь скролл), берём ImageMagick и считаем разницу в пикселях. Цель после Mode A — ≤2% различий.

Что считается допустимым:

  • Куки-попап на одной версии есть, на другой нет (зависит от localStorage).
  • Кастомный курсор / hover-эффекты — JS подгружает их асинхронно.
  • Анимации в hero — на момент скриншота могут быть в разных фазах.

Что не допустимо:

  • Шрифты выглядят иначе → не подключился Montserrat.
  • Сдвиг блоков по вертикали → не загрузился какой-то Тильда-CSS.
  • Пустые блоки слайдеров → init-скрипты не выполнились (это та самая ловушка из этапа 2).

Эта дисциплина превращает миграцию из «как-то так перенесли, вроде работает» в детерминированный процесс: либо скриншоты совпали, либо нет; если нет — точно знаем, что сломано.

Где мне помогает Claude Code (и где не помогает)

Раз я обещал в названии статьи разобрать AI-сторону — разберу честно.

Claude Code — это AI-помощник в редакторе кода, который умеет читать структуру проекта, искать по файлам, писать и редактировать код по моим инструкциям. Я использую его на всех этапах миграции, кроме принятия архитектурных решений.

Где он реально снимает рутину:

  • Этап 0, дискавери. Прошу его построить таблицу «URL → файл → title» по всей выгрузке. Дальше прошу извлечь FAQ-блоки из всех страниц с типом data-record-type="1119". Дальше — выписать в один документ все ссылки, ведущие наружу, чтобы я их проверил. Это часы экономии.
  • Этап 1, перенос ассетов. Команды cp, проверки, что всё на месте. Тут он почти не нужен, но он не ошибается с путями — а человек ошибается.
  • Этап 2, перенос страниц. Это самая большая экономия. Прошу его сгенерировать карту страниц page-map.ts по 31 странице сайта — заполнить URL, файл, title, description из самих Тильда-HTML. Я потом прохожу глазами, корректирую заголовки и описания под маркетинговую логику. Без него каждая страница занимала бы 5 минут — итого 2–3 часа на простое заполнение. С ним — 30 минут на проверку.
  • Этап 3, SEO-обвязка. Прошу: «вот страница услуги, добавь к ней MedicalProcedureSchema с полями procedureType, priceFrom, howPerformed, preparation. Поля возьми из текста на странице. Где не уверен — поставь TODO». Делает в 90% случаев нормально, остальные 10% — мои правки.
  • Этап 6, Mode B. «Перепиши тильдовский FAQ-аккордеон на нативный <details> с теми же CSS-классами». Делает ровно. Проверяю скриншотами.

Где он не помогает и где можно сильно ошибиться, если ему доверять:

  • Архитектурные решения. Двухфазная модель, последовательность этапов, выбор стека, договорённость с клиентом — это всё мои решения. AI про них ничего не знает.
  • Контент. Никогда не прошу его «придумать FAQ» или «дописать описание услуги». В медицине это путь к галлюцинациям и проблемам с регулятором. FAQ берём дословно из Тильда-HTML, описания — у клиента или из старого сайта.
  • Проверка качества. Скриншот-сравнение и проверки в Search Console — это не делегируется. AI может сказать «всё ок», когда не ок.

Поэтому в названии моей системы AI стоит как ускоритель рутины, а не как «AI делает миграцию». Магии нет, есть инженерия.

Реальный кейс — Зубчик.ру, в процессе

Я сейчас веду эту миграцию на стоматологии Зубчик.ру. Сайт живёт на Тильде, два филиала в Москве (Сокол и Митино), 31 страница, 590 картинок, 28 JS-бандлов Тильды. Цифр «до/после» по SEO у меня пока нет — миграция в процессе, в продакшн ещё не выкатили. Когда выкатим и пройдут 4 недели мониторинга — добавлю их прямо сюда.

Что уже видно по технической части на staging:

  • HTML каждой страницы получается чище и легче, чем у Тильды (но в Mode A всё ещё с тильдовскими бандлами CSS — это нормально, чистка в Mode B).
  • SEO-обвязка работает: на каждой странице услуги MedicalProcedureSchema, на странице со списком услуг — BreadcrumbList, на FAQ-блоках — FAQPage. Тильда такого из коробки не даёт, всё это — управляемое наполнение.
  • Скриншоты Astro-страниц совпадают с Тильда-эталонами в среднем на 98–99%. На отдельных страницах есть разница в анимациях hero — допустимо.

Когда сделаем переключение DNS, опубликую отдельный технический разбор с цифрами Lighthouse, временем загрузки и динамикой органики после переезда.

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

Чтобы не было слишком гладко — кратко перечислю реальные баги. На каждом из них я провёл от 40 минут до пары часов.

1. Слайдеры и карты пустые. Перенесли HTML — а блок отзывов пустой, FAQ не открывается, Яндекс.Карта не отрисована. Причина — те самые <script> в теле страницы, которые не выполняются после set:html. Решение описано в этапе 2.

2. Шрифт Montserrat не загружается. Заголовки выглядят иначе. Тильда CSS пишет font-family: 'Montserrat', но сам шрифт не подключает — на их инфраструктуре он подгружается отдельным механизмом, который в выгрузку не попадает. Решение — добавить <link> на Google Fonts вручную в layout.

3. Картинки 404. Lazy-load Тильды читает реальный путь из data-original, а не из src. Если этот атрибут не переписать на новый префикс — браузер видит 404. Решение — переписывать data-original параллельно с src.

4. JSON-LD выводится сломанным. Astro по умолчанию эскейпит фигурные скобки в <script type="application/ld+json">, и валидатор Schema.org показывает кашу. Решение — использовать атрибут set:html явно, плюс is:inline, иначе Astro попробует обработать содержимое как ES-модуль.

5. После добавления страницы — 404 на dev-сервере. getStaticPaths() кэшируется, dev-сервер не видит свежих изменений в карте страниц. Решение — rm -rf .astro node_modules/.vite && npm run dev. Тратит 10 секунд, но если не знать — провисишь полчаса.

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

Готовые промпты и команды — забирайте себе

Это часть, которую обычно ищут отдельно. Забирайте.

Команда скриншот-сравнения

После каждой существенной правки — снимаем такой же набор для Astro и сравниваем попиксельно через ImageMagick:

# Astro работает на :4321
brew install imagemagick   # один раз

CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

declare -A PAGES=(
  ["home"]="/"
  ["implant"]="/uslugi-i-tseny/implantatsiya-zubov"
  ["doctors"]="/vrachi"
)

for name in "${!PAGES[@]}"; do
  url="${PAGES[$name]}"
  "$CHROME" --headless=new --disable-gpu --no-sandbox \
    --window-size=1280,8000 --hide-scrollbars \
    --virtual-time-budget=30000 \
    --run-all-compositor-stages-before-draw \
    --screenshot=_screenshots/$name-astro.png \
    "http://127.0.0.1:4321$url"

  diff=$(compare -metric AE -fuzz 5% \
    _screenshots/$name-tilda.png \
    _screenshots/$name-astro.png \
    _screenshots/$name-diff.png 2>&1)
  total=$(magick identify -format '%[fx:w*h]' _screenshots/$name-tilda.png)
  pct=$(echo "scale=2; $diff * 100 / $total" | bc)
  echo "$name: $pct% различий"
done

Цель после Mode A — ≤2% различий на всех страницах. Файл _screenshots/$name-diff.png показывает красным, где именно разница — открываете и смотрите глазом.

Промпт 1 — построить page-map.ts по Тильда-выгрузке

Это самая большая экономия времени. Открываете Claude Code (или Cursor) в папке с Astro-проектом, рядом лежит tilda-archive/, и пишете:

В папке tilda-archive/ лежит выгрузка сайта стоматологии в Тильде.
Прочитай sitemap.xml и составь массив PAGES для src/data/page-map.ts
по этой схеме:

{
  url: <pathname из <loc>>,
  file: <имя pageNNNN.html, ищи по совпадению canonical>,
  title: <содержимое <title> из HTML, очищенное от « | Зубчик» в конце>,
  description: <содержимое <meta name="description"> из HTML>,
  type: <определи по URL: 'home' / 'service' / 'blog' / 'contacts' / 'about'>,
  breadcrumbs: <построй из URL: «Главная» → раздел → текущая страница>,
}

Если на странице есть FAQ-блок (data-record-type="1119" или 1120),
извлеки вопросы и ответы дословно в поле faq: [{ q, a }, ...].
Не выдумывай ничего, чего нет в HTML. Если поле не получается заполнить —
ставь TODO с пояснением.

На 30 страниц получаете готовый файл за минуту вместо двух часов ручного заполнения. Дальше обязательно проходите глазами — AI ошибается в логике хлебных крошек на нестандартных URL и иногда срезает важные слова из title.

Промпт 2 — добавить Schema.org к странице услуги

Открой файл src/data/page-map.ts и найди запись с url
"/uslugi-i-tseny/implantatsiya-zubov".

Добавь к ней поле procedure со схемой MedicalProcedure:
- name: название процедуры
- procedureType: 'http://schema.org/SurgicalProcedure'
  (или TherapeuticProcedure / DiagnosticProcedure — выбери по смыслу)
- priceFrom: минимальная цена в рублях, число
- howPerformed: краткое описание процесса (1-2 предложения)
- preparation: что делать перед процедурой (1-2 предложения)
- followup: что после (1-2 предложения)

Все значения бери ТОЛЬКО из текста соответствующей страницы
в tilda-archive/. Если какого-то поля нет в исходнике —
ставь null и комментарий // TODO: уточнить у клиента.
Ничего не придумывай.

Главное в промпте — фраза «бери только из текста» и «не придумывай». Без неё AI с радостью сочинит «followup: пациент в течение 2 недель использует ополаскиватель» — выглядит правдоподобно, но это галлюцинация. В медицине так нельзя.

Промпт 3 — переписать тильдовский блок на нативный Astro-компонент (Mode B)

В public/tilda/ Тильда использует FAQ-блок с типом t-zoomer и
classами t585__title, t585__textwrapper. Открой страницу
/uslugi-i-tseny/implantatsiya-zubov на http://localhost:4321
и посмотри, как этот блок выглядит и работает.

Перепиши его на нативный Astro-компонент src/components/FAQBlock.astro
с такими требованиями:
1. Используй <details> и <summary> вместо JS-аккордеона.
2. CSS-классы оставь идентичными тильдовским (t585__title и т.д.) —
   стилизация должна работать без правок.
3. Принимай items: { q: string, a: string }[] через props.
4. Никакого jQuery, никакого внешнего JS.

После замены подключи компонент на нужной странице вместо
тильдовского HTML. Я после этого сниму скриншоты до/после
и проверю совпадение.

Этот промпт — типовой для Mode B. Меняете блок (t585 → ваш Astro), снимаете скриншоты, прогоняете команду сравнения сверху. Если разница > 1% — откатываете и разбираете, что отличается.

Системный промпт «правила миграции» в начале каждой сессии

Если вы работаете с Claude Code или Cursor над миграцией — положите в корень проекта файл CLAUDE.md (или .cursorrules) с базовыми правилами. AI читает их автоматически и не нарушает:

# Правила работы над миграцией Tilda → Astro

## Что НЕ делать
- Не править HTML внутри tilda-archive/. Это эталон, он read-only.
- Не менять CSS-классы Тильды (t396, t585 и т.д.) на этапе Mode A.
  Это сломает стили, которые приходят из Tilda CSS.
- Не «улучшать дизайн на ходу». Сначала пиксельная копия,
  любой редизайн — после Mode A.
- Не выдумывать контент. FAQ, цены, адреса — только из tilda-archive/
  или из явно переданного брифа.

## Что делать всегда
- На любую новую страницу — сразу прописывать SEO-поля
  в src/data/page-map.ts (title, description, breadcrumbs, type).
- Schema.org JSON-LD — через <script ... set:html={...} is:inline />,
  иначе валидатор покажет ошибки.
- После любого изменения вёрстки — напоминать пользователю
  снять скриншоты и сравнить с эталоном.

## Стек проекта
- Astro 6 (mode static)
- Без Tailwind на этапе Mode A
- @astrojs/sitemap, @astrojs/mdx
- Picture/Image из astro:assets — только в Mode B+

Эти 30 строк дают AI правильные рамки и убирают 80% «творческих идей», на которых он начинает чинить то, что не сломано.

Чеклист перед мержем (smoke-тест)

Минимальный — занимает 5 минут на страницу:

  • Главная: hero, преимущества, услуги, врачи, отзывы — все блоки видны и не пустые.
  • Услуга: FAQ-аккордеон раскрывается, цены отображаются, форма открывает попап.
  • Контакты: карта Яндекс отрисована (не серый прямоугольник).
  • Console (DevTools → Console) пуст — 0 ошибок JS.
  • Network (DevTools → Network) — 0 запросов с 404.
  • Валидатор Schema.org показывает 0 ошибок.
  • Lighthouse Mobile: SEO=100, Performance ≥ 80 (после Mode A), ≥ 95 (после Mode B+).

Когда лучше не переезжать

Я обещал балансность. Вот ситуации, когда миграцию не надо начинать, даже если кажется, что пора.

  • Сайт ещё работает, но скоро понадобится редизайн. Не надо делать миграцию и редизайн одновременно. Сделайте миграцию сейчас, дайте ей пожить 2–3 месяца, потом редизайнте на новой платформе. Иначе вы получите оба риска на один проект.
  • У вас один лендинг и нет планов растить контент. Подписка на Тильду 18 000 ₽ в год. Миграция стоит от 100к (с моим участием). Окупаемость — никогда. Оставайтесь на Тильде.
  • У вас нет ресурса на 3–6 месяцев параллельной работы. Mode B — это месяцы. Если все ресурсы на маркетинг и продажи и нет возможности выделять 5–10 часов в неделю на технические правки — миграция повиснет в полузавершённом состоянии.
  • Сайт на 80% — каталог товаров с интеграцией со складом. Тут отдельная история: возможно, вам нужна не миграция, а сразу e-commerce-платформа (1С-Битрикс, Shopify, кастомное решение). Это отдельный разговор, не «давайте уйдём с Тильды».
  • У вас нет понятной маркетинговой картины «зачем переезжаем». Если ответ только «потому что говорят, что Тильда плохая» — не надо. Это плохой повод для месяцев работы и риска для бизнеса.

Чек-лист первых шагов, если решились

Если вы дочитали досюда и думаете «ок, мне надо», вот короткий список первых шагов — независимо от того, делаете вы миграцию сами, нанимаете команду или зовёте меня.

  1. Сделайте экспорт ZIP из Тильды и положите в надёжное место. Это ваша точка возврата. Не надейтесь, что Тильда сохранит вам историю — экспорт делайте перед каждым серьёзным изменением.
  2. Снимите бенчмарки. Lighthouse Performance, LCP, INP, CLS на 5–7 ключевых страницах (главная, главная услуга, страница «о нас», страница контактов, одна-две статьи блога). Это ваша точка отсчёта.
  3. Сохраните позиции в выдаче. Возьмите 30–50 ключевых запросов, по которым вы стоите в Яндексе и Google. Зафиксируйте текущие позиции в Search Console / Метрике / любом сервисе мониторинга. После переезда будете сравнивать с этим списком.
  4. Постройте карту URL. Все живые страницы Тильды (берите из её sitemap.xml). Для каждой — будущий URL на новом сайте. Лучше всего — оставить URL без изменений: это снимает 80% рисков с SEO. Если меняете URL — каждая пара «старый → новый» должна быть однозначной.
  5. Решите со стеком. Astro для контентных сайтов и блогов, Next.js для динамики и интеграций, WordPress + кеш для редакции без технической команды. Я обычно беру Astro — на нём dim-osi.ru тоже сделан.
  6. Решите по 301-редиректам. На стороне нового хостинга нужно настроить серверные редиректы со старых URL Тильды на новые. Это стандартная конфигурация для nginx, Caddy, CloudFront. Если у вас сайт лежит на статичном хостинге без серверной части — нужны решения через Cloudflare Workers или подобные. 301, не 302 [9].
  7. Готовьтесь, что после переключения DNS будут 2–4 недели мониторинга. Search Console, Метрика, прозвон ключевых страниц. Если в это время в команде запланирован отпуск или большой запуск — лучше отложить миграцию.

Если всё это звучит сложно

Это и есть сложно. Миграция нормального бизнес-сайта без обвала SEO — это не проект на выходные. Это 1–3 месяца методичной работы, плюс ещё пара месяцев Mode B на постепенный рефакторинг. И эту работу нельзя срезать «давайте быстрее, как-нибудь» — иначе вы или потеряете SEO, или останетесь с полусломанным новым сайтом.

Поэтому если вы дочитали и понимаете, что:

  • Тильда вас ограничивает,
  • но вы не хотите рисковать накопленным трафиком,
  • и вам нужна не «вёрстка с нуля», а методичный, контролируемый переезд —

напишите мне в Telegram. Сделаю бесплатный аудит вашей текущей Тильды, скажу честно: пора переезжать или ещё нормально, и если пора — какие тут будут реальные сроки и риски. Если решите делать со мной — методику разворачиваем на вашем проекте; если пойдёте к подрядчикам, эти же 6 этапов и критерии приёмки помогут вам контролировать процесс и не дать его уронить.

И, конечно, дальше я обновлю эту статью, когда выкачу Зубчик.ру на Astro в продакшн и пройдут первые 4 недели мониторинга. Будут реальные цифры — добавлю прямо сюда.

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

Источники

  1. [1]
    Site moves with URL changes Google Search Central
  2. [2]
    Core Web Vitals Google web.dev
  3. [3]
    Redirects and Google Search Google Search Central
  4. [4]
  5. [5]
  6. [6]
    Why Astro? Astro Documentation
  7. [7]
  8. [8]
  9. [9]
    HTTP redirections MDN Web Docs
  10. [10]

Как со мной связаться

Расскажите задачу —
разберём, как её решать.

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

Обычно отвечаю в течение рабочего дня. На часовых поясах от Калининграда до Владивостока проверено — пишите, когда вам удобно.