Перейти к содержимому

Сервер, TTFB и кеширование: скорость до того, как началась вёрстка

  • Половина проблем со скоростью решается до того, как речь зайдёт о вёрстке и картинках. Если сервер думает секунду, оптимизация фронтенда этого уже не отыграет.
  • Ориентир по TTFB от web.dev: до 0,8 секунды — хорошо, больше 1,8 — плохо. Сам по себе TTFB не входит в Core Web Vitals, но он предшествует LCP и служит для него потолком.
  • Кеш — самый дешёвый прирост скорости из существующих. Одно включённое кеширование страниц на тяжёлой CMS часто даёт больше, чем неделя работы над фронтендом.
  • Сжатие обязательно, но не всё подряд: brotli и gzip дают эффект на тексте, а на уже сжатых форматах вроде JPEG и WebP только тратят процессор.
  • HTTP/2 полезен людям, но ранжирующего бонуса у него нет, и Googlebot одинаково спокойно ходит по HTTP/1.1 и HTTP/2.
  • По HTTP/3 Googlebot не ходит вообще. Внедрять его стоит ради пользователей, а не ради робота, и точно не вместо поддержки более старых протоколов.
  • Медленный сервер бьёт по обходу. Google снижает частоту сканирования, если сайт отвечает медленно или отдаёт ошибки, и повышает, когда всё быстро.
  • Чаще всего сайт тормозят не его собственные файлы, а чужие: аналитика, чат-виджет, пиксели рекламных кабинетов и виджеты соцсетей.
  • Первое, что нужно понять при разборе — где именно теряется время. Одинаковые пять секунд на бэкенде и на картинках лечатся совершенно по-разному.

Разговор про скорость сайта обычно начинается не с того конца. Открывают PageSpeed Insights, видят красное, идут сжимать картинки и минифицировать CSS. Иногда помогает. Но регулярно оказывается, что браузер полсекунды просто ждёт, пока сервер соизволит ответить, и вот эти полсекунды не убрать никакой минификацией — они потрачены до того, как браузер получил хоть один байт.

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

TTFB — время до первого байта DNS Соединение и TLS Ожидание бэкенда Передача Отрисовка сеть и сервер: хостинг, кеш, протокол браузер: вёрстка, картинки, скрипты

Слева — то, за что отвечают хостинг, сервер и сеть. Справа — то, за что отвечает фронтенд. Инструменты вроде PageSpeed показывают общую картину, но разбирать проблему надо по этим двум половинам отдельно, иначе получается лечение симптомов.

Time to First Byte — время от начала запроса до момента, когда пришёл первый байт ответа. В него входят редиректы, поиск DNS, установка соединения с TLS-рукопожатием и, главное, работа самого бэкенда.

Пороги web.dev простые: до 0,8 секунды хорошо, от 0,8 до 1,8 — есть над чем работать, больше 1,8 — плохо. Отдельно оговорено, что TTFB не входит в Core Web Vitals, то есть формально держаться в зелёной зоне не обязательно.

На это стоит смотреть трезво. TTFB не метрика ранжирования, но он потолок для всего остального: LCP физически не может случиться раньше, чем пришёл первый байт. Если TTFB полторы секунды, то уложить LCP в рекомендованные 2,5 почти нереально — на всю отрисовку остаётся секунда. Так что TTFB чинят не ради самого TTFB, а ради Core Web Vitals и живых людей.

Синяя часть на схеме — обычно самая жирная и самая дешёвая в исправлении. Типичные причины по частоте:

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

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

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

Внешние вызовы в момент отдачи страницы. Когда бэкенд при генерации страницы ходит в чужой API — за курсом валют, остатками на складе, отзывами, — вы наследуете чужую скорость и чужие сбои. Такие вызовы надо кешировать или уносить в фон.

Редиректы. Каждый лишний переход в цепочке добавляется к TTFB целиком. Ещё одна причина схлопывать цепочки, помимо тех, что описаны в статье про коды состояния HTTP.

Кеш — это ответ на вопрос «зачем делать заново то, что уже сделано». Уровней три, и они не заменяют друг друга.

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

Кеш браузера. Управляется заголовком Cache-Control и решает, будет ли повторный визит бесплатным. Рабочая схема: статике с хешем в имени файла (app.4f2b9c.js) ставим срок в год и immutable, а HTML не кешируем надолго, иначе люди будут видеть старую версию сайта. Хеш в имени решает проблему обновления: поменялся файл — поменялся адрес.

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

Текстовые файлы — HTML, CSS, JavaScript, JSON, SVG — сжимаются в разы. Brotli делает это лучше gzip при сопоставимой скорости, gzip остаётся универсальным запасным вариантом. Настраивается один раз на уровне сервера и дальше просто работает.

Чего делать не надо: сжимать то, что уже сжато. JPEG, PNG, WebP, AVIF, видео и архивы прогонять через brotli бессмысленно — размер не изменится, а процессорное время потратится. У любого нормального сервера есть список типов для сжатия, и в нём должны быть только текстовые форматы.

Проверить просто: в инструментах разработчика в заголовках ответа должен быть content-encoding: br или gzip. Если его нет — вы прямо сейчас отдаёте людям файлы в несколько раз больше нужного.

Здесь много фольклора, поэтому по фактам.

HTTP/2 позволяет передавать много файлов по одному соединению и избавляет от старых костылей вроде склейки всех скриптов в один файл. Googlebot ходит по HTTP/2 с конца 2020 года, но Google прямо говорит, что ранжирующего преимущества у этого нет — роботу всё равно, по какому протоколу забирать страницу. Включать стоит ради пользователей.

HTTP/3 работает поверх QUIC и заметно лучше себя ведёт на нестабильных мобильных сетях. Важная деталь, которую стоит знать перед внедрением: Googlebot по HTTP/3 не сканирует. Это не аргумент против — людям он полезен, а на Core Web Vitals может повлиять положительно, — но это аргумент за то, чтобы не отключать поддержку старых протоколов ради него.

Соединение со сторонними доменами. Каждый чужой домен на странице — это отдельный поиск DNS, отдельное соединение и отдельное TLS-рукопожатие. Если сторонний ресурс критичен для первого экрана, помогает preconnect. Но лучший способ не платить за соединение — не делать его вовсе.

Иногда правильный ответ — не оптимизировать, а переехать. Признаки, что дело в хостинге, а не в сайте: TTFB плавает в зависимости от времени суток, простая статическая страница отдаётся так же медленно, как тяжёлая, в панели видны упоры в лимиты процессора, а поддержка на вопросы отвечает предложением перейти на тариф подороже.

Проверяется грубо, но надёжно: положите на сервер обычный HTML-файл без CMS и замерьте время ответа. Если голая статика отдаётся за 600 мс, дело не в вашем коде.

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

Что с этим делать: раз в квартал открывать список загружаемых скриптов и спрашивать по каждому, кто им пользуется и когда последний раз смотрел эти данные. Обычно половину можно снять без потерь. Остальное — грузить асинхронно и по возможности после взаимодействия, а не в момент открытия страницы.

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

И ещё одна связь, о которой забывают: затяжные 5xx в часы нагрузки — это не «сайт немножко полежал». Для поисковика это сигнал, что сайту нездоровится, и при повторении он начинает выбрасывать страницы из индекса.

Полевые данные против лабораторных. Замер на вашем ноутбуке по быстрому проводу показывает лучший случай. Реальность видна в отчёте Search Console по Core Web Vitals и в CrUX — там данные живых визитов, включая мобильный интернет в метро.

Разложение по фазам. Вкладка Network в инструментах разработчика показывает время ожидания ответа отдельно от времени загрузки. Смотреть надо именно на разбивку, а не на итоговую цифру.

Простая проверка из командной строки. curl -w "%{time_starttransfer}" -o /dev/null -s <адрес> покажет время до первого байта без всякого фронтенда. Прогоните несколько раз в разное время суток — увидите, стабилен ли ваш хостинг.

Логи сервера. Если в них пишется время генерации страницы, вы увидите не средние показатели, а конкретные медленные URL. Обычно выясняется, что тормозит не весь сайт, а три типа страниц.

  • TTFB на основных типах страниц укладывается в 0,8 секунды.
  • Кеширование страниц на сервере включено, динамические страницы из него исключены.
  • Кеш сбрасывается по событию (изменилась цена, вышел материал), а не только по таймеру.
  • Cache-Control для статики с хешем в имени — длинный срок и immutable.
  • HTML не закеширован в браузере надолго.
  • Включено сжатие brotli или gzip для текстовых форматов и не включено для картинок и видео.
  • Сайт работает по HTTP/2 или новее, поддержка старых протоколов не сломана.
  • Цепочек редиректов перед выдачей страницы нет.
  • Внешние API не вызываются синхронно при генерации страницы.
  • Список сторонних скриптов пересматривался за последние полгода.
  • Проверена реальная скорость на полевых данных, а не только в лаборатории.
  • 5xx под нагрузкой отсутствуют, есть внешний мониторинг доступности.