SEO

Как ускорить сайт: от замера в DevTools до архитектуры

Evgeniy Volkov

SEO34 мин чтения

Как ускорить сайт: от замера в DevTools до архитектуры

Скорость сайта это не про «красиво в отчёте», это про деньги и позиции. Медленная страница теряет посетителя раньше, чем он увидел твой товар, а поисковик тихо опускает её ниже. При этом почти все советы, как ускорить сайт, из топа выдачи либо устарели, либо сводятся к «поставь плагин кеширования и не думай». Плагин симптом приглушит, причину не тронет. В этой статье мы сделаем иначе: сначала руками найдём конкретный тормоз в DevTools, потом починим базу, а дальше разберём, как со скоростью работают на масштабе, где живут тысячи страниц и три языка. Без магии, с реальными числами.

Меня зовут Евгений Волков, я fullstack-разработчик, и скорость для меня не хобби, а часть работы над каждым проектом. Я разгонял и лендинги, и e-commerce, и международную платформу на 1000+ страниц. Поэтому пишу не «10 универсальных советов», а показываю рабочий процесс инженера: как замерить, как прочитать цифры, где обычно спрятан тормоз и чем его чинят. Числа в статье настоящие, из моей практики и из документации. Где данных нет, я так и говорю.

Кратко: как ускорить сайт по шагам

Если нужен план сразу, вот он одним взглядом. Ниже каждый пункт раскрыт подробно, с воркфлоу и числами.

ШагЧто делаешьЧто чинит
1. ЗамерьPageSpeed + Lighthouse + Яндекс.Метрика (полевые данные)Понимаешь, где реально тормоз
2. Найди рукамиDevTools: Performance, Network, CoverageКонкретную причину, а не догадку
3. Почини базуКартинки WebP/AVIF, шрифты, кеш, Brotli, чистка скриптов70% типовых проблем
4. Разбери бандлCode splitting, ленивые чанки, tree-shaking, nuxt analyzeТяжёлый первый экран, плохой INP
5. Выбери архитектуруSSR / SSG / ISR под задачу, аккуратная гидрацияПотолок скорости и TTFB
6. ЗакрепиМониторинг реальных пользователей, ловля регрессийЧтобы не замедлился обратно

Нормы, к которым идём: LCP ≤ 2,5 с, INP ≤ 200 мс, CLS ≤ 0,1. Это целевые пороги Core Web Vitals, дальше объясню каждую метрику и как её читать.

Почему скорость решает деньги и позиции

Начнём с честного «зачем», чтобы дальше было ясно, ради чего мы возимся с миллисекундами.

Скорость бьёт по двум фронтам сразу. Первый это конверсия. Человек не ждёт. Каждая лишняя секунда загрузки это часть посетителей, которые закрыли вкладку, не дождавшись. На медленном сайте растёт показатель отказов, падает глубина просмотра, дешевеет трафик, за который ты уже заплатил. Тут скорость это прямые деньги, без всякого SEO.

Второй фронт это ранжирование. И Google, и Яндекс учитывают скорость страницы как один из факторов. Google оформил это в Core Web Vitals, три метрики, которые описывают реальный опыт пользователя. Яндекс тоже смотрит на скорость и время до загрузки. Оговорюсь честно: скорость это не главный рычаг ранжирования. Контент, ссылки и соответствие интенту весят больше. Но при прочих равных быстрый сайт обгоняет медленный, а на конкурентной тематике «прочие равные» встречаются постоянно.

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

Отдельно проговорю про мобильных. Больше половины трафика в рунете это телефоны, и часто не флагманы, а бюджетные Android на нестабильной сети в метро или за городом. То, что на твоём рабочем ноутбуке грузится за секунду, на таком устройстве может тянуться пять. А поисковики давно оценивают скорость именно по мобильной версии, не по десктопной. Так что когда я говорю «сайт медленный», я имею в виду не свой Mac с быстрым интернетом, а реального человека с телефоном в руке и одной палкой сети. На него и ориентируемся, потому что это и есть твой покупатель.

Вывод простой. Скорость это не косметика ради зелёной цифры в PageSpeed. Это выручка, позиции и индексация одним пакетом. Поэтому её стоит не «подкрутить разок», а держать под контролем. Как именно, разбираем дальше.

Как измерить скорость и как читать цифры

Первое правило оптимизации: не оптимизируй вслепую. Пока не замерил, любые правки это гадание на кофейной гуще. Сначала цифры, потом руки.

Инструментов много, но реально нужны четыре.

PageSpeed Insights. Веб-сервис Google. Вбиваешь URL, получаешь и лабораторный прогон (Lighthouse), и, если у сайта есть трафик, полевые данные реальных пользователей (CrUX). Бесплатно, быстро, для первого взгляда идеально.

Lighthouse в DevTools. Тот же движок, но прямо в Chrome. Открываешь DevTools, вкладку Lighthouse, жмёшь «Analyze». Удобно гонять локально во время работы, не выкладывая сайт наружу.

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

Chrome DevTools (Performance, Network, Coverage). Это уже не «оценка», а расследование. Тут ловят конкретный тормоз руками. Про этот воркфлоу будет отдельный раздел ниже, он и есть главное отличие инженерного подхода от «поставил плагин».

Про цифры коротко, потому что дальше вся статья про починку. Нормы Core Web Vitals на 2026 год такие: LCP ≤ 2,5 с, INP ≤ 200 мс (старую FID Google убрал в марте 2024, если сервис всё ещё меряет FID, он отстал), CLS ≤ 0,1. Отдельно TTFB, ответ сервера, в идеале ≤ 0,8 с. И одна оговорка, на которой все спотыкаются: лабораторный прогон (Lighthouse, PageSpeed) и полевые данные живых пользователей (CrUX, Яндекс.Метрика) часто расходятся, и верить надо полевым, тому что видят люди. Как читать каждую метрику, чем мерить и что из инструментов работает в России, я подробно разобрал в отдельной статье, как проверить скорость сайта. Тут дальше про то, как эти цифры чинить.

Мой рабочий порядок такой. Сначала PageSpeed для общей картины и полевых данных. Потом Lighthouse локально, чтобы быстро крутить гипотезы. И DevTools, когда надо докопаться до конкретной секунды. К нему и переходим.

DevTools: как найти тормоз руками

Вот раздел, ради которого стоило писать статью. Ни один гайд из топа не показывает реальный воркфлоу разбора в DevTools, все ограничиваются «прогоните Lighthouse». А Lighthouse только говорит, что болит. Где именно болит, показывают три другие панели. Разберём по шагам, как это делаю я.

Шаг 1. Lighthouse как отправная точка

Открой сайт в Chrome, зайди в DevTools (F12), вкладка Lighthouse. Выбери Mobile, категорию Performance, жми Analyze. Через полминуты получишь оценку и список проблем: «Reduce unused JavaScript», «Properly size images», «Eliminate render-blocking resources». Это не приговор, это карта. Lighthouse говорит куда копать, но не копает за тебя.

Записал главные жалобы. Дальше идём проверять их руками, потому что синтетика ошибается, а панели показывают правду по твоему конкретному сайту.

Шаг 2. Network waterfall: кто блокирует первый экран

Вкладка Network. Поставь галку Disable cache, выбери троттлинг Fast 3G или Slow 4G (чтобы увидеть картину как у пользователя с телефоном), перезагрузи страницу. Получишь водопад запросов. Смотрим на три вещи.

TTFB. Наведи на первый запрос документа, посмотри Timing. Если «Waiting for server response» большой, тормозит сервер, а не фронтенд, и картинки тут ни при чём. Это сигнал копать в бэкенд, кеш и хостинг.

Render-blocking ресурсы. Ищи CSS и синхронный JS в самом начале водопада, до первой отрисовки. Каждый такой файл браузер обязан скачать и обработать, прежде чем показать страницу. Это главные враги LCP. Их либо откладывают (defer/async), либо инлайнят критическую часть.

Порядок и вес. Видно, что грузится первым, что самое тяжёлое, что тянется с чужих доменов. Часто именно здесь ловишь неожиданное: шрифт на 300 КБ, аналитику, которая блокирует рендер, картинку-баннер в полном разрешении.

Шаг 3. Performance: где горит main-thread

Самая мощная панель и самая недооценённая. Вкладка Performance, включи троттлинг CPU (4x или 6x slowdown, эмуляция слабого телефона), жми запись, перезагрузи, останови через пару секунд после загрузки.

Ты получишь флейм-график (flame chart) активности главного потока. Что искать:

Long tasks. Любая задача длиннее 50 мс помечается красным уголком. Это куски JavaScript, которые надолго занимают main-thread, и именно из-за них страница «залипает» и не реагирует на клики. Long tasks это главная причина плохого INP. Кликаешь на задачу и видишь, какая функция или скрипт её породил.

Main-thread activity. Смотришь, чем поток занят между загрузкой и интерактивом. Если там сплошной жёлтый (scripting), значит браузер давится JavaScript вместо того, чтобы рисовать страницу. Часто это гидрация тяжёлого SPA или сторонний скрипт.

Однажды я так ловил тормоз, который Lighthouse показывал как «высокий Total Blocking Time», но не говорил причину. В Performance оказалось, что виджет чата инициализировал сам себя синхронно на старте и держал main-thread почти секунду. Убрали синхронную инициализацию, отложили загрузку до взаимодействия, и INP вернулся в зелёную зону. Без Performance-панели это была бы неделя гаданий.

Шаг 4. Coverage: сколько кода вообще не используется

Открой командную палитру (Cmd/Ctrl+Shift+P), набери Coverage, запусти. Перезагрузи страницу. Панель покажет по каждому CSS и JS файлу, какой процент кода реально выполнился, а какой просто скачался и лёг мёртвым грузом.

Часто видишь красное: 70–80% CSS фреймворка не используется на этой странице, половина JS-бандла не нужна для первого экрана. Это прямое указание, где резать. Неиспользуемый CSS чистят (PurgeCSS, правильная сборка Tailwind), лишний JS выносят в ленивые чанки. Про это подробно в разделе про бандл.

Сведу воркфлоу в таблицу, чтобы было под рукой как шпаргалка.

ПанельЧто ищемНа какую метрику влияетЧем чиним
LighthouseСписок общих жалоб, карта проблемОбщая оценкаОтправная точка, не лечение
NetworkTTFB, render-blocking, тяжёлые запросыLCP, TTFBdefer/async, кеш, сжатие картинок
PerformanceLong tasks, занятость main-threadINP, TBTРазбить/отложить JS, убрать тяжёлые скрипты
CoverageНеиспользуемый CSS/JSLCP, размер бандлаЧистка, ленивые чанки, tree-shaking

Вот и весь воркфлоу: Lighthouse сказал где болит, Network показал что блокирует, Performance показал где горит поток, Coverage показал что выкинуть. Четыре панели, ноль плагинов, полная картина. Замечу главное отличие от типового совета «прогони PageSpeed». PageSpeed выдаёт балл и общие рекомендации, но он не покажет, что именно на твоём сайте виджет чата душит main-thread, а шрифт грузится с чужого домена с задержкой в 400 мс. Это видно только руками, в этих панелях. Поэтому инженер начинает не с балла, а с расследования. Дальше чиним найденное.

Базовая оптимизация, которая реально работает

Теперь конкретные фиксы. Это база, которую можно и нужно сделать почти на любом сайте, и она закрывает процентов семьдесят проблем. Пройдусь по блокам и по ходу поправлю советы, которые в топе выдачи устарели.

Картинки

Картинки почти всегда самый тяжёлый вес страницы. С них и начинаем.

  • Современные форматы. Переводи JPEG и PNG в WebP или AVIF. WebP легче JPEG примерно в полтора-два раза при том же качестве, AVIF ещё легче. Это самый дешёвый и самый заметный выигрыш.
  • Отдавай под размер экрана. Используй srcset и sizes, чтобы телефон грузил маленькую версию, а не десктопную картинку шириной 2000 пикселей, ужатую в CSS до 400.
  • Ленивая загрузка. Всё, что ниже первого экрана, помечай loading="lazy". Браузер не будет тянуть эти картинки, пока пользователь до них не докрутит.
  • Задавай размеры. Ширина и высота (или aspect-ratio) в атрибутах спасают от прыжков вёрстки и держат CLS в норме.
  • Сжимай заранее. Не надейся, что «браузер разберётся». Прогоняй через sharp, squoosh или imagemin на этапе сборки.
  • Приоритет для LCP-картинки. Главную картинку первого экрана помечай fetchpriority="high" и НЕ вешай на неё loading="lazy". Частая ошибка: ленят вообще все картинки подряд, включая ту, что и есть LCP-элемент, и своими руками замедляют главный показатель. Лениво грузим то, что ниже сгиба, а не то, что человек видит первым.

Шрифты

Шрифты недооценивают, а они блокируют отрисовку текста и ломают LCP с CLS.

  • font-display: swap. Чтобы текст показался сразу шрифтом по умолчанию, а кастомный подхватился, когда догрузится. Без этого пользователь смотрит на пустое место (FOIT).
  • preload ключевого шрифта. <link rel="preload" as="font"> для того начертания, что нужно на первом экране. Браузер начнёт тянуть его раньше.
  • Сабсеттинг. Оставь в файле шрифта только те символы, что реально нужны (кириллица + латиница), выкинь остальные. Вес падает в разы.
  • Локальный хостинг. Свои шрифты быстрее и надёжнее внешних CDN, плюс без сторонних запросов.

Код: CSS и JavaScript

  • Минификация. CSS и JS должны идти в прод сжатыми, без пробелов и комментариев. Любой нормальный сборщик делает это сам, проверь, что оно включено.
  • defer и async. Скрипты, не нужные для первой отрисовки, помечай defer (выполнится после парсинга) или async (выполнится как загрузится). Синхронный <script> в <head> без этих атрибутов блокирует рендер.
  • Critical CSS. Стили первого экрана инлайнят прямо в HTML, остальное грузят асинхронно. Так первый экран рисуется без ожидания внешнего CSS.
  • Мёртвый код. Тот самый неиспользуемый CSS/JS из Coverage. Чистим.

Сторонние скрипты: тихий убийца скорости

Про этот пункт молчат почти все, а он в моей практике один из самых частых виновников плохого INP. Речь про чужой JavaScript: аналитика, пиксели рекламных кабинетов, виджет чата, карта, кнопки соцсетей, онлайн-консультант. Каждый такой скрипт это отдельный запрос на чужой домен и, что хуже, чужой код, который крутится в твоём main-thread и которым ты не управляешь.

Проблема в том, что маркетолог добавляет их «на пять минут», а платит за это скорость. Три пикселя, чат и тепловая карта легко съедают секунду отзывчивости, и в Performance-панели ты увидишь их как долгие задачи с чужих доменов.

Что с этим делать:

  • Ревизия. Открой, что реально стоит на сайте, и выкини то, чем никто не пользуется. Часто половина скриптов это забытое наследие прошлых кампаний.
  • Отложенная загрузка. Чат и второстепенную аналитику грузи не на старте, а по взаимодействию или через requestIdleCallback, когда браузер освободился. Пользователю чат не нужен в первую секунду.
  • Атрибуты async/defer на всех сторонних тегах, чтобы они не блокировали рендер.
  • Считай цену. Каждый новый скрипт это осознанное решение «эта секунда отзывчивости стоит этих данных?», а не рефлекс «добавим на всякий».

Подсказки браузеру: preconnect, dns-prefetch, preload

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

<!-- Заранее устанавливаем соединение с доменом, откуда придут шрифты/API -->
<link rel="preconnect" href="https://fonts.example.com" crossorigin>
<!-- Только резолвим DNS, полегче preconnect -->
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- Тянем критичный ресурс первого экрана раньше -->
<link rel="preload" as="font" href="/fonts/main.woff2" type="font/woff2" crossorigin>

preconnect заранее делает DNS-резолв, TCP-рукопожатие и TLS для домена, с которого скоро придёт важное. preload поднимает приоритет конкретного ресурса первого экрана (шрифт, ключевая картинка). Только не переусердствуй: если запрелоадить десять вещей, ты не ускоришь ничего, а размажешь приоритеты. Preload это скальпель для одного-двух критичных ресурсов, а не ковровая бомбёжка.

Кеш и сжатие на сервере

  • Cache-Control: immutable. Для статики с хешем в имени файла (app.a1b2c3.js) ставь Cache-Control: max-age=31536000, immutable. Браузер закеширует навсегда и не будет перезапрашивать. При изменении меняется хеш, значит и URL.
  • ETag. Для контента, который может меняться, ETag позволяет браузеру спросить «изменилось?» и получить лёгкий 304 Not Modified вместо полной перекачки.
# Пример заголовков для статики с хешем в имени
Cache-Control: public, max-age=31536000, immutable

# Для HTML, который меняется
Cache-Control: no-cache

Сжатие и протокол: тут топ выдачи особенно отстал

Вот два места, где старые советы прямо вредят.

+Плюсы

  • Brotli вместо голого Gzip. Многие гайды до сих пор пишут только про Gzip. Brotli сжимает текст (HTML, CSS, JS) плотнее Gzip на 15–25%, и его поддерживают все актуальные браузеры. Включай Brotli, Gzip оставляй как fallback для древних клиентов.
  • HTTP/2 (или HTTP/3). Мультиплексирование: много файлов едут по одному соединению параллельно, без очереди. Почти все хостинги это уже умеют, проверь, что включено.

Минусы

  • Domain sharding больше не нужен. Старый трюк «раскидай статику по нескольким поддоменам, чтобы обойти лимит соединений» под HTTP/1.1 работал. Под HTTP/2 он вредит: лишние соединения, лишние TLS-рукопожатия, потеря приоритизации. Если тебе всё ещё советуют шардить домены, совет из 2015 года.
  • Gzip без Brotli. Не ошибка, но упущенная выгода. Плотнее сжатие бесплатно лежит рядом.

CDN

CDN раздаёт статику с ближайшего к пользователю узла. Нужен, когда аудитория размазана по стране или миру. Если все посетители в одном регионе и сервер рядом, выигрыш почти нулевой. Важный нюанс для России: убедись, что у CDN есть точки присутствия внутри страны, иначе запросы поедут в Европу и станет хуже, а не лучше.

Сведу базу в чеклист, чтобы было с чем сверяться.

Чеклист базовой оптимизации:

  • Картинки в WebP/AVIF, с srcset/sizes и loading="lazy"
  • У картинок заданы размеры (нет скачков CLS)
  • Шрифты: font-display: swap, preload ключевого, сабсет
  • CSS и JS минифицированы
  • Некритичные скрипты с defer/async
  • Critical CSS инлайнится, остальное асинхронно
  • Мёртвый CSS/JS вычищен (проверено Coverage)
  • Статика с хешем отдаётся с Cache-Control: immutable
  • Включён Brotli (Gzip как fallback)
  • HTTP/2 или HTTP/3 работает
  • CDN, если аудитория широкая географически

Это фундамент. Он снимает большинство проблем на большинстве сайтов. Но есть тормоза, которые базой не лечатся, потому что сидят в архитектуре бандла. О них дальше, и вот тут начинается инженерия, которой в топе нет вообще.

Бандл: code splitting, ленивые чанки и tree-shaking

Базовые фиксы дают быстрый результат, но упираются в потолок, если сама сборка кода собрана плохо. Главный симптом: один жирный JavaScript-бандл, который браузер обязан скачать, распарсить и выполнить, прежде чем страница станет живой.

Почему это убивает первый экран. Пока грузится и парсится мегабайтный бандл, main-thread занят, страница не реагирует, INP в красной зоне, а LCP ждёт, пока JS дорисует контент. Ты можешь идеально сжать картинки, но если на входе висит 2 МБ JavaScript, всё остальное не спасёт.

Лечится это архитектурой сборки. Разберём инструменты.

Route-level code splitting. Не грузи код всех страниц сразу. Каждый роут должен тянуть только свой чанк. Nuxt и Next делают это из коробки при файловой маршрутизации, но легко сломать, затащив всё в глобальные импорты. Проверяй.

Dynamic import(). Тяжёлые куски, которые нужны не сразу, грузи по требованию. Модалка, которая открывается по клику. Библиотека графиков на странице, куда доходит не каждый. Редактор, тяжёлый календарь, карта.

// Плохо: библиотека графиков в основном бандле у всех
import Chart from 'heavy-chart-lib'

// Хорошо: подтянется, только когда реально нужна
const Chart = await import('heavy-chart-lib')

Ленивые компоненты. В Nuxt префикс Lazy (<LazyHeavyWidget />) откладывает загрузку компонента, пока он не понадобится. Комбинируешь с ленивой гидрацией, и тяжёлый виджет не грузит первый экран.

Tree-shaking. Сборщик выкидывает неиспользуемый экспорт из библиотек. Работает, только если импортируешь точечно (import { debounce } from 'lodash-es'), а не тянешь пакет целиком (import _ from 'lodash'). Одна неаккуратная строка импорта тащит в бандл сотни килобайт мёртвого кода.

Анализ бандла. Команда nuxt analyze (или webpack-bundle-analyzer / визуализатор Rollup) рисует карту: что и сколько весит в сборке. Открываешь и сразу видишь жирные куски, которые не должны там лежать. Это как Coverage, только для сборки. Без такого анализа оптимизация бандла это гадание.

На что смотрю в этой карте первым делом. Ищу vendor-чанк, куда сборщик складывает сторонние библиотеки, и проверяю, что в нём нет неожиданных тяжеловесов. Классика жанра: целиком затащенный moment с локалями всех стран мира, lodash вместо точечных импортов, дублирующиеся версии одной библиотеки, которые притащили разные зависимости. Второе, что проверяю, это не попал ли код, нужный только на одной странице, в общий чанк, который грузится у всех. Третье, нет ли иконок или SVG, зашитых прямо в JS вместо спрайта. Одна такая находка порой снимает с первого экрана двести-триста килобайт, и это без единой правки логики, просто аккуратный импорт.

Пример из моей практики, но не про JS-бандл, а про ассеты, где тот же принцип виден нагляднее. На evolkov.tech в героe крутится 3D-сцена. Исходные модели весили дико: desktop_pc 16 МБ, монитор acer 9 МБ. Для первого экрана это приговор. Я прогнал их через gltf-transform с draco-компрессией геометрии и переводом текстур в webp. Результат на графике.

Вес 3D-моделей на evolkov.tech до и после сжатия
draco-компрессия геометрии плюс webp-текстуры. desktop_pc с 16 МБ до 1,1 МБ, acer с 9 МБ до 221 КБ. Декодер draco лежит локально, чтобы не тянуть его со стороннего CDN.

Принцип одинаковый и для 3D, и для JavaScript: не тащи в первый экран то, без чего он отрисуется. Сжимай, дели, откладывай. Разница между «сайт грузится 8 секунд» и «сайт грузится за секунду» чаще всего живёт именно здесь, в архитектуре бандла и ассетов, а не в настройках кеша.

i18n на масштабе: почему локали топят старт

Отдельная боль, про которую не пишет никто, а на серьёзных проектах она встаёт в полный рост. Мультиязычность.

Сразу тезис: интернационализация не тормоз сама по себе. Тормозом её делает наивная реализация. И вот как это выглядит.

Классический подход i18n-библиотек: загрузить все переводы всех языков в бандл при старте приложения. На маленьком сайте с двумя десятками строк на трёх языках это незаметно. А теперь представь платформу, где тысячи страниц, у каждой свои тексты, свои описания, свои FAQ, и всё это на нескольких языках. Словари распухают до мегабайтов. И весь этот вес прилетает браузеру на старте, до того как пользователь вообще что-то сделал.

Я видел это не в теории. На международной контентной платформе, которую я разгонял, было 275 файлов локалей общим весом около 8,6 МБ (EN и RU). Симптом был характерный: серверный рендеринг отдавал готовый HTML, страница визуально появлялась, но кликать по ней было нельзя. Интерактив мёртв минутами, потому что браузер давился загрузкой и разбором гигантских словарей, прежде чем гидрация оживит страницу. SSR есть, а сайт как будто завис. Худший вид тормоза: выглядит загруженным, а не работает.

Решение было не «уменьшить переводы», их нельзя выкинуть, они и есть контент. Решение архитектурное: своя per-page i18n-загрузка. Вместо одного гигантского словаря на всё приложение осталось маленькое ядро с общими строками (навигация, кнопки, подвал), а переводы конкретной страницы стали подтягиваться отдельным лёгким чанком по роуту. Ядро ужалось примерно до 115 КБ, остальное грузится ровно тогда, когда пользователь на страницу зашёл.

Вес локалей, тянущийся к старту, до и после per-page загрузки
Было около 8,6 МБ словарей на 275 файлов, тянувшихся к старту приложения. Стало ядро около 115 КБ, а переводы страницы подгружаются по роуту. Общий объём контента тот же, изменилась только стратегия загрузки.

После этого интерактив вернулся туда, где ему место: сразу. Это как раз тот случай, когда проблему не решить ни плагином, ни настройкой сервера, ни сжатием картинок. Нужно менять то, как приложение грузит данные. Полный разбор этого проекта я собрал в кейсе международной платформы калькуляторов: 1000+ страниц, два языка, и вот эта самая перф-архитектура локалей под капотом.

Мораль для тех, кто делает мультиязычный сайт. Проверь, как твоя i18n-библиотека грузит переводы. Если она тащит все языки и все страницы в один бандл, это бомба замедленного действия, которая рванёт, как только контента станет много. Грузи лениво, по роуту, с маленьким ядром. Тогда i18n не стоит тебе ни миллисекунды лишнего старта.

Архитектура: SSR, SSG и ISR решают больше, чем плагины

Поднимемся ещё на уровень выше. Самое сильное решение по скорости принимается до первой строчки оптимизации, когда выбираешь модель рендеринга. Оно бьёт мощнее любого плагина кеширования, потому что определяет, что вообще браузер получит в ответ.

Разложу три подхода простыми словами.

SSR (server-side rendering). Сервер на каждый запрос собирает готовый HTML и отдаёт браузеру. Пользователь видит контент сразу, поисковик получает готовую страницу, LCP хороший. Плата: сервер работает на каждый запрос, TTFB зависит от его скорости, нужен живой сервер и кеш. Подходит, когда контент часто меняется и завязан на пользователя.

SSG (static site generation). Страницы собираются один раз на этапе билда в статические HTML-файлы. Дальше их раздаёт хоть простой файловый сервер, хоть CDN. Это самое быстрое, что бывает: TTFB почти нулевой, ломаться на рантайме нечему, нагрузки на сервер нет. Плата: при изменении контента нужна пересборка, и на миллионе страниц билд становится долгим. Идеально для контента, который меняется не ежесекундно.

ISR (incremental static regeneration). Гибрид. Страницы статические, но сервер умеет пересобирать отдельные из них по расписанию или по запросу, не трогая остальные. Свежесть почти как у SSR, скорость почти как у SSG. Плата: сложнее в настройке и требует поддержки со стороны хостинга или фреймворка.

МодельTTFBСвежесть контентаНагрузка на серверКому
SSRСредний, зависит от сервераВсегда свежийВысокая, на каждый запросЛичные кабинеты, часто меняющиеся данные
SSGМинимальныйНа момент билдаНулеваяКонтент, лендинги, блоги, витрины
ISRМинимальныйСвежий по расписаниюНизкаяБольшие каталоги, где важна и скорость, и свежесть

Ключевая мысль: тяжёлая CMS, которая на каждый запрос собирает страницу из PHP и базы, а потом разгоняется серверным кешем, почти всегда проигрывает по TTFB заранее собранной статике. Кеш CMS это попытка догнать статику, начиная с отставания. Если контент позволяет, честнее сразу отдавать статику, а не догонять кешем медленный рендер.

Пример у тебя перед глазами. Сам evolkov.tech собран как статика: nuxt generate превращает сайт в набор HTML-файлов, которые лежат на моём сервере и раздаются напрямую через Caddy. Нет прослойки, нет рендера на каждый запрос, нет базы, которая может лечь. TTFB минимальный, ломаться на рантайме нечему. Для сайта-визитки и блога это оптимальная модель, и я выбрал её осознанно, а не по инерции «все делают SSR».

Цена гидрации: почему SSR не бесплатный

Тут есть ловушка, в которую попадают, поверив, что «поставил SSR и всё летает». Сервер отдал готовый HTML, пользователь увидел контент, красота. Но страница ещё не живая. Чтобы кнопки нажимались, а формы работали, браузеру нужно скачать JavaScript и «оживить» уже нарисованный HTML. Это и есть гидрация.

И вот тут прячется коварство. Между «вижу контент» и «могу кликать» лежит окно, пока грузится и выполняется JS. Если бандл жирный, окно растягивается, и получается ровно та картина, что я разбирал на i18n: страница выглядит загруженной, а не отвечает. Формально LCP хороший, а INP в красной зоне, и пользователь злится, тыкая в мёртвую кнопку.

Лечится это тем, что гидрируют не всё и не сразу. Ленивая гидрация откладывает оживление тяжёлых компонентов до того момента, когда они попадут в зону видимости или пользователь до них дотронется. Островная архитектура (islands) идёт дальше и гидрирует только интерактивные куски, оставляя статичный контент статичным навсегда. Nuxt умеет и то и другое. Смысл один: не заставляй браузер оживлять весь тяжёлый JavaScript ради пары кнопок в подвале.

Оговорюсь честно про пределы. SSR и SSG не волшебство. Если поверх правильного рендеринга висит жирный бандл и тяжёлая гидрация, выигрыш от серверной отрисовки съедается на клиенте. Архитектура рендеринга задаёт потолок скорости, но не освобождает от работы с бандлом. Поэтому реальный результат это связка: правильная модель рендеринга плюс аккуратный бандл плюс базовая оптимизация. Всё вместе, а не по отдельности.

Почему самоделки и коробки не тянут 1000+ страниц

Пока страниц десятки, тормоза не видно. Проблемы масштаба вылезают, когда контента становятся тысячи, и вот тут коробочные решения и наивные самоделки начинают трещать.

Разберу типовые точки отказа.

Билд-тайм. Если сайт статический, а страниц много, наивная сборка пересобирает всё на каждое изменение. На тысяче страниц билд из минут превращается в десятки минут. Нужен инкрементальный подход (тот самый ISR или умное кеширование сборки), иначе деплой становится мучением.

Asset-pipeline на каждую страницу. Каждая страница тянет картинки, которые надо сжать, форматы, которые надо сгенерировать, превью. Без нормального пайплайна обработки ассетов на масштабе это либо забивает билд, либо отдаёт неоптимизированное в прод.

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

Cache invalidation. Обновил товар, а на витрине старая цена, потому что где-то в цепочке кешей застряла прошлая версия. На масштабе инвалидация кеша это отдельная инженерная задача, а не галочка. Известная шутка про то, что это одна из двух труднейших вещей в программировании, ровно про это.

N+1 запросы. Классика бэкенда. Страница со списком дёргает базу по разу на каждый элемент вместо одного запроса на всех. На десяти элементах незаметно, на тысяче страница висит секундами. Ловится профилировщиком запросов, лечится нормальными выборками.

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

То же с базой. Пока в каталоге сто товаров, кривой запрос отвечает за миллисекунды, и никто не замечает N+1. На пятидесяти тысячах позиций тот же запрос кладёт страницу на секунды, а под нагрузкой роняет сервер. Это ловится профилировщиком и лечится нормальной выборкой с джойнами и индексами, но только если кто-то вообще знает, что туда надо смотреть. Самоделка и коробка обычно не знают.

Коробочные CMS и собранные на коленке движки спотыкаются об это постоянно, потому что их архитектура не рассчитана на объём. Они прекрасно работают на сотне страниц и осыпаются на тысячах.

Как это выглядит, когда сделано правильно, покажу на реальных числах. Международная контентная платформа, которую я упоминал: 1094 страницы контента, 201 статья в блоге, два языка, и всё это не только не тормозит, но и растёт в поиске. По Ahrefs домен добрался до DR 39, что высоко для молодого сайта в тяжёлой YMYL-тематике. Трафик за месяц по Яндекс.Метрике вырос на +47%, и главный источник роста это поиск, а не соцсети. Платформу цитируют AI-поисковики (ChatGPT, Copilot), а это отдельный маркер того, что контент и структура на масштабе собраны здорово. Тысяча с лишним быстрых, проиндексированных, ранжирующихся страниц это не случайность, а следствие архитектуры, которая изначально рассчитана на объём.

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

Вайб-кодинг и ИИ: инструмент дирижёра, а не дирижёр

Тема, которую нельзя обойти в 2026 году. Раз речь про ускорение работы, стоит честно поговорить про ускорение самого инженера с помощью ИИ. И заодно про то, где вайб-кодинг помогает, а где ломает скорость сайта.

Сразу расставлю точки. Я не против ИИ, наоборот. ИИ-агенты (Claude Code, ChatGPT с Codex, Gemini CLI) реально ускоряют разработку, и я сам работаю с ними каждый день. Написать бойлерплейт, накидать тесты, разобрать незнакомую библиотеку, отрефакторить рутину: тут они экономят часы. Это мощный инструмент, и отрицать его глупо.

+Плюсы

  • Бойлерплейт и рутина. Накидать компонент, форму, типовой CRUD, конфиг ИИ делает быстро и без ошибок усталости.
  • Тесты и документация. Покрыть код юнит-тестами, описать функцию, разобрать чужую библиотеку это его сильная сторона.
  • Точечный рефакторинг под присмотром, когда ты знаешь, что хочешь, и проверяешь каждый шаг.
  • Скорость гипотез. Быстро набросать вариант, посмотреть, отбросить. Как черновик, который потом доводишь сам.

Минусы

  • Не видит замысел целиком. Работает с тем куском, что перед глазами, и не знает, зачем в соседнем файле сделано «странно».
  • Считает намеренные перф-компромиссы багами и «чинит» их, ломая скорость.
  • Уверенно наводит красоту там, где красота вредит: сносит ручной чанкинг, кеш, отложенную гидрацию.
  • Чем дальше от типовой задачи и ближе к оптимизации, тем чаще вредит, а не помогает.

Но у инструмента есть граница, и на оптимизации она видна особенно резко. ИИ-агенты работают как оркестр под дирижёром. Ты задаёшь направление, держишь в голове всю картину и архитектуру, проверяешь результат. Они виртуозно исполняют партии, но не видят замысел целиком. Сами по себе, без дирижёра, они не понимают, почему в конкретном месте сделано именно так.

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

Живой пример, инженерный и правдоподобный. Допустим, ответ API или целую страницу намеренно закешировали через SWR или ISR, а то и просто заголовками. Почему? Потому что данные меняются редко, а TTFB и скорость важнее секундной свежести. Это осознанный компромисс, принятый инженером, который знает продукт. В коде причина не написана, она в голове у человека.

Теперь запускаем на этот код ИИ-агента без контекста. Он видит: данные отдаются устаревшие, стоит странный кеш, код «неидеальный». Логика агента: это баг, надо починить. Он убирает кеш или переписывает на чистый SSR без кеширования. Итог: TTFB подскочил, скорость просела, полезная оптимизация уничтожена. Хуже того, агент не останавливается: он по цепочке трогает связанное, «улучшает» соседний код, и ломает ещё в паре мест, потому что не знает, где проходят невидимые провода.

То же самое с любым намеренным перф-решением. Заинлайненный руками критический CSS выглядит как «дублирование стилей, надо вынести». Отложенная гидрация выглядит как «почему компонент не гидрируется сразу, это баг». Ручной чанкинг локалей, тот самый, что спас старт на 275 файлах, выглядит как «зачем так сложно, давай стандартный импорт». Без понимания «почему так» ИИ причёсывает код под общие best practices и сносит ровно те решения, которые держали скорость.

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

Так что на вопрос «можно ли ускорить сайт нейросетью» честный ответ: нейросетью можно ускорить работу инженера, который ускоряет сайт. Инструмент в руках дирижёра. Без дирижёра оркестр играет вразнобой.

Частые ошибки, которые я вижу в аудитах

За годы аудитов набор тормозов повторяется до узнаваемости. Соберу самые частые, потому что, возможно, твой сайт спотыкается ровно об это, и половину можно поправить сегодня же.

Ленивая загрузка на картинке первого экрана. Человек прочитал «lazy ускоряет» и навесил loading="lazy" на всё, включая баннер героя. Итог: браузер откладывает загрузку именно того, что должно появиться первым, и LCP проседает. Лениво грузим то, что ниже сгиба, не то, что видно сразу.

Слайдер-карусель на главной. Красиво в макете, больно в проде. Пять-десять полноразмерных фото, которые грузятся все и сразу, плюс тяжёлая библиотека слайдера, плюс автопрокрутка, дёргающая рендер. Я не помню аудита с каруселью на весь экран, где она бы не была в тройке тормозов. Чаще всего её вообще не листают.

Шрифты с внешнего домена без подготовки. Подключили набор начертаний с чужого CDN, без preconnect, без сабсета, без font-display. Текст мигает, LCP ждёт шрифт, лишние запросы на сторонний домен. Свои шрифты, локально, с сабсетом почти всегда быстрее.

Весь JavaScript одним куском. «Работает же». Работает, пока сайт маленький. Как только контента и функций становится много, единый бандл превращается в кирпич на старте, и никакой кеш этого не спасает. Про лечение был целый раздел выше.

Аналитика и чат синхронно в head. Классика. Три счётчика, пиксель и виджет консультанта грузятся первыми, блокируют рендер и держат main-thread, пока пользователь смотрит на белый экран. Всё это спокойно живёт с defer или откладывается до взаимодействия.

Кеш выключен «чтобы видеть свежее». Разработчик отключил кеширование на время работы и забыл вернуть. Или CMS по умолчанию не кеширует ничего. Каждый запрос собирается заново, TTFB высокий на ровном месте. Проверяй заголовки на проде, а не только на глаз.

Тема-комбайн на все случаи жизни. Купили тему, которая «умеет всё»: и магазин, и портфолио, и блог, и лендинг. Внутри код под все сценарии сразу, из которых ты используешь один. Вес тащишь за все. Лучше лёгкая тема под свою задачу, чем универсальный комбайн.

Прыгающая вёрстка от поздних баннеров. Реклама, куки-баннер, промо-плашка вставляются в DOM с задержкой и сдвигают контент под уже читающим пользователем. Это разрушает CLS и бесит людей. Резервируй под них место заранее, задавай размеры контейнерам.

Заметь общую нить. Почти каждая ошибка это не «сайт плохо написан», а «сделали удобно для себя и забыли про пользователя с телефоном». Скорость возвращается, когда начинаешь смотреть на сайт его глазами, а не глазами разработчика на быстрой машине.

Как закрепить результат: мониторинг реальных пользователей

Разогнать сайт это половина дела. Вторая половина не дать ему замедлиться обратно. Скорость деградирует незаметно: добавили баннер, подключили ещё один аналитический скрипт, обновили библиотеку, и вот LCP тихо уполз в жёлтую зону. Поэтому за скоростью не «прогоняют Lighthouse раз в полгода», а следят постоянно.

Ключевое различие мы уже проговорили: лабораторные данные врут про реальность, полевые говорят правду. Значит и мониторить надо полевые.

CrUX и PageSpeed для международного и Google-трафика. Показывают агрегированные Core Web Vitals реальных пользователей по 75-му перцентилю. Если поехало вниз, увидишь.

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

RUM (Real User Monitoring). Если хочется глубже, ставят сбор метрик прямо с живых сессий: у каждого пользователя замеряются LCP, INP, CLS и отправляются в аналитику. Тогда видно не средний градус по больнице, а распределение, и можно ловить, что на дешёвых Android всё плохо, хотя на твоём ноутбуке идеально.

Практический смысл мониторинга в ловле регрессий. Настроил слежение, договорился с собой о порогах (LCP ≤ 2,5 с, INP ≤ 200 мс, CLS ≤ 0,1), и когда метрика их пробивает, идёшь разбираться в DevTools по тому же воркфлоу, что в начале статьи. Скорость это не разовая акция, а состояние, которое поддерживают. Один тяжёлый скрипт, добавленный маркетологом «на пять минут», способен откатить месяц работы.

Когда чинить самому, а когда звать инженера

Честная рамка на финал, без попытки затащить всех в платную работу. Часть проблем реально решается своими руками, часть требует инженера. Разделю прямо.

Чини сам, если:

  • Проблема в картинках. Сжать, перевести в WebP, добавить lazy это делается без разработчика, инструментами и плагинами.
  • Нужен базовый кеш и сжатие. Включить Brotli, выставить Cache-Control, поставить кеш-плагин на CMS это уровень настроек.
  • Надо почистить очевидный мусор. Убрать десяток неиспользуемых плагинов, выкинуть лишний внешний скрипт, поправить шрифты.
  • Ты на конструкторе. Там всё равно тонко не порулишь, выжми что дают штатные настройки и не мучайся.

Зови инженера, если:

  • Тормоз в бандле или гидрации. Code splitting, ленивые чанки, tree-shaking, разбор nuxt analyze это уже разработка, а не настройка.
  • Проблема в архитектуре рендеринга. Выбор между SSR, SSG и ISR, переезд с медленной CMS, настройка кеширования на уровне приложения.
  • Мультиязычность топит старт. Per-page загрузка локалей это инженерная работа, плагином не решается.
  • Сайт большой и растёт. Тысячи страниц, каталог, N+1, инвалидация кеша, долгий билд. Тут нужен тот, кто держит всю картину.
  • Базовая оптимизация сделана, а зелёной зоны нет. Значит тормоз глубже, чем настройки, и его надо искать в коде.

Я делаю ровно это как fullstack-разработчик: беру сайт, замеряю по воркфлоу из статьи, показываю, где именно теряются секунды, и чиню то, что даёт максимум эффекта за минимум работы. Не «оптимизацию ради красивой цифры», а конкретные секунды, которые видит пользователь и учитывает поиск. Разбор скорости входит в мою услугу технического SEO, она стартует от 10 000 ₽: начинаем с честного аудита, где я показываю тормоза на твоих цифрах, а дальше вместе решаем, что чинить в первую очередь.

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

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

Поделиться:
Все статьи