Кеширование и серверный пререндеринг (SSR)
Приложение на Next.js (React) имеет из коробки серверный пререндеринг страниц. Это очень хорошо для SEO, и для FMP метрики — время от открытия страницы до первого значимого контента.
Казалось бы, фреймворк умеет в SSR из коробки, зачем вообще писать статью? Есть один нюанс, мы рендерим страницу дважды: сначала на сервере, потом на клиенте, и общее время загрузки неизбежно увеличивается.
CSR, SSR и Dynamic Rendering
Для начала небольшое отступление про клиентский, серверный и динамический рендеринги.
CSR | SSR |
---|---|
|
|
Гугл рекомендует использовать динамический рендеринг, если важно SEO.
Для работы динамического отображения ваш сервер должен распознавать поисковых роботов (например, проверяя агент пользователя). Запросы от роботов передаются средству отображения, а запросы от пользователей обрабатываются обычным образом. При необходимости средство динамического отображения возвращает версию контента, которая может быть обработана роботом, например статическую HTML-страницу. Динамическое отображение можно включить как для всех страниц, так и только для некоторых.
Важно не забывать, что любое приложение или сайт, мы, разработчики, в первую очередь делаем для пользователей, а не машин. Так почему бы не ускорить отображение сайта для не-роботов?
Включаем серверный рендеринг в обоих случаях: и для пользователей, и для машин.
После включения SSR оказалось, что время загрузки сайта неприлично выросло. Хотя мы добивались обратного. Почему так?
Если раньше сервер мгновенно отдавал небольшой html-файл, а дальше браузер сам загружал javascript, css и рендерил страницу, то теперь мы еще ждем, пока сервер отрендерит страницу, отправит большой html на клиент.
В чем же тогда преимущество SSR? Не использовать ли нам динамический рендеринг? Ответ зависит от вашего приложения. В нашем случае, пользователи получали контент намного раньше, ведь уже на втором шаге в случае SSR видна вся страница: со стилями и готовой разметкой. Общее время загрузки увеличилось, но оно ощущается не так критично, как время до первого значимого контента.
Уменьшаем время SSR
Если правильно приготовленный реакт на клиенте рендерится быстро, то реакт на сервере рендерится, по серверным метрикам, непростительно медленно.
Значит, нужно кешировать процесс серверного рендеринга. А вот это уже «из коробки» в next.js не работает.
Сам процесс и механизм кеширования — тема отдельной статьи. Есть множество библиотек для express-js, просто библиотек для кеширования чего угодно.
Казалось бы, кеш настроен, страницы стали грузиться мгновенно.
Кеш прогревали?
Время открытия страницы — важный показатель. И не только с точки зрения потенциального клиента, который может уйти не дождавшись загрузки. Поисковикам также не нравится медленные сайты.
На нашем проекте есть множество страниц с результатами фильтрации. И если главная страница открывается в 99,999% из кеша, то остальные страницы посещаются реже, а процент открытий на холодую, несравненно, выше. Поисковикам явно такое не понравится.
А тот пользователь, который будет менять настройки фильтра и каждый раз прогревать кеш для других, явно не позавидует своей участи.
Если сервер по рендеру реакта смасштабирован горизонтально, то случаев прогрева будет больше.
Прогревать при старте весь кеш нецелесообразно: такая операция может занять минуты. Элегантного решения здесь нет. То, к чему стоит стремиться — на основе результатов аналитики прогревать кеш по мере падения популярности страниц. Напомню, с большим количеством параметров фильтрации, зашитых в URL, количество страниц в кеше может быть неприлично большим. Поэтому стоит устанавливать некоторый порог.
Vary: user-agent
Но вот незадача, нужно кешировать не только по URL, но и по User Agent. Не нужно же нам рендерить сложные меню для мобильных девайсов, чтобы они еще и занимали процессорное время и сервера, и мобильного устройства, чтобы потом скрывать элементы по `display: none`?
Хорошо, берем express, улучшаем кеширующий middleware. В зависимости от User-Agent, рендерим 3 разных макета. Проверяем — работает.
Проверяем результат в Google Lighthouse — и, все мерцает. Проверяем результат в Google Lighthouse — и, все мерцает. Осталось только указать заголовок `Vary: user-agent`.
Из википедии:
Клоакинг — (от англ. cloak — мантия, маска, прикрытие) прием «чёрной» поисковой оптимизации, заключающийся в том, что информация, выдаваемая пользователю и поисковым роботам на одной и той же странице, различается.
Но переживать не стоит: выдержка из рекомендаций гугла:
Googlebot не считает динамическую отрисовку маскировкой, если на всех версиях страницы представлен одинаковый контент. <...> Если при динамическом отображении пользователям и роботам показывают совершенно разные версии страницы, это считается маскировкой.
Результат
Каждое устройство (мобильное или десктоп) стало получать соответствующий статический контент. Нет никаких мерцаний стилей или контента. При незаметном увеличении общего времени загрузки (10-40ms), мы добились значительного прироста скорости доставки значимого контента, при этом удовлетворив требованиям по SEO для поисковых машин.
kalyukdo
А зачем рендерить на сервере html для таких страниц?
ведь пользователь уже загрузил ваш Js, он уже у него в кэше, когда он меняет фильтры, серверный рендер не нужен, а для поисковика достаточно будет страницы с десктопной версии для индексации.
я обычно юзаю такую логику, если пользователь загрузил JS с последней версией, я ставлю куку, и после этого времени отключаю вообще серверный рендер для таких пользователей, а также серверный рендер не нужен авторизованным пользователям
Carduelis Автор
Если использовать рекламу по низкочастотным запросам, то пользователь будет прогревать кеш, что очень плохо скажется на CTR. Да и просто переходы из огранической выдачи, которые выше 95% процентиля, будут негативно сказываться как на пользовательском опыте, так и не позициях в выдаче.
интересный вариант, кстати
kalyukdo
тогда действительно можно выключить серверный рендер, поставьте перед express nginx + добавьте gz + cache и вы на статике выиграете в разы больше чем на SSR.
Из моего опыта, SSR нужен только для индексации поисковиками, это можно отлавливать все по тем же заголовкам, затем можно разбить бандл (самого приложения) на файлы размерами не более 100кб, а вендоры(React и ему подобные) вынести в отдельный бандл, в общем, в результате вы получите реальное большое количество статики, которая почти всегда будет лежать в кеше браузера, и если уже говорить про ретаргет, то вы выиграете в 100% случаев, а про первый визит нужно смотреть метрики но по ощущениям тоже будет быстрее чем SSR