Команда JavaScript for Devs подготовила перевод статьи о том, сколько трафика реально выдерживает сайт на Next.js. Автор провёл нагрузочные тесты, сравнил VPS и выделенный сервер, проверил разницу между предрендерингом и SSR и сделал вывод: для сайтов с потенциальными всплесками трафика предрендеринг — спасение, а SSR может стать бутылочным горлышком.
Я часто говорил что-то вроде: «Предварительно отрендеренный сайт легко обслужит сотни одновременных пользователей», потому что, честно говоря, я никогда не видел, чтобы такой сайт падал.
Но сколько он реально может выдержать? Справится ли мой сайт, если вдруг попадёт на главную Hacker News? Как это сравнивается с серверным рендерингом? И стоит ли вообще из кожи вон лезть, чтобы избежать SSR?
Я пытался найти конкретные цифры по производительности Next.js, но достоверных данных оказалось удивительно мало. Поэтому я провёл собственные тесты на своём сайте — и результаты оказались неожиданными. А на следующий день мой пост про то, как Google Translate ломает React, оказался на главной Hacker News.
Как раз после того, как я выяснил (спойлер!) — мой сайт, скорее всего, бы не справился.
Предварительно отрендеренный сайт на VPS
Первым делом я хотел выяснить, выдержит ли мой сайт резкий наплыв посетителей, например, если он попадёт на главную Hacker News.
Для теста я написал простой скрипт нагрузочного тестирования на k6, чтобы замерить максимальное количество запросов в секунду, которые может обработать мой сервер. Скрипт был максимально прост: он многократно запрашивал одну и ту же страницу, дожидаясь завершения каждого запроса. Полное описание теста можно найти в приложении.

Запустив скрипт, я выяснил, что один инстанс Next.js, обслуживающий мою полностью предварительно отрендеренную главную страницу на X4 BladeVPS от TransIP, способен обрабатывать 193 запроса в секунду, при этом только 63% запросов отвечают быстрее, чем за 500 мс.
Важно подчеркнуть: каждый запрос в этом тесте — это лишь вызов одной предварительно отрендеренной страницы Next.js. Никакие дополнительные ассеты не запрашиваются. Реальный посетитель делает ещё 60+ запросов. То есть трёх одновременных посетителей может быть достаточно, чтобы сервер оказался на грани перегрузки.
Скажу честно, гораздо меньше моих ожиданий.
Анатомия визита
Чтобы понять, почему для загрузки сайта требуется более 60 запросов, давайте посмотрим, что именно происходит, когда кто-то открывает мою главную страницу:
DNS-запрос и SSL-рукопожатие.
Первичный запрос (1: 20,3 КБ): браузер запрашивает HTML-документ.
CSS (1: 1,6 КБ): браузер парсит HTML и запрашивает основной CSS-файл.
На этом этапе страница уже полностью стилизована и видна, а пользователь может взаимодействовать с большинством элементов — ещё до загрузки JavaScript. Единственное, чего пока нет:
Изображения в первом экране (5: 6,8 КБ): браузер загружает изображения, попадающие в зону видимости.
Обычно таких изображений немного, и общий объём к этому моменту составляет 50–100 КБ. Именно тогда посетитель видит «полноценную» страницу. Поскольку большинство интерактивных элементов реализованы нативно (средствами браузера), пользователь уже может взаимодействовать со страницей (например, переходить по ссылкам).
Остальные запросы «отложенные» — они низкого приоритета и не блокируют рендеринг. К ним относится большая часть трафика:
Аналитика (1: 2,5 КБ): запрос скрипта Plausible.
JavaScript (13: 176,8 КБ): загрузка JS, необходимого для основной страницы.
Отложенные изображения (1: 22,8 КБ): изображения, частично видимые в первом экране.
Плейсхолдеры данных (17: 11,9 КБ): данные-заглушки для изображений ниже первого экрана.
Favicon (2: 52,2 КБ): фавиконки разных размеров.
Предзагруженные ресурсы (22: 136,1 КБ): ресурсы для потенциальной навигации (чтобы переход был мгновенным).
В сумме получается 63 запроса и 434,5 КБ.
Это показывает, что Next.js генерирует заметно больше запросов и трафика по сравнению с классическим серверным рендерингом или статическим сайтом без Next.js. С одной стороны, это даёт мне, как разработчику, полный контроль над UX и отличный DX. С другой — пользователю и серверу приходится обрабатывать куда больше трафика. Справедливости ради, <500 КБ для современного сайта не так уж и много, особенно учитывая, что страница становится доступной к взаимодействию уже после первых 100 КБ.
React Server Components (RSC) могли бы немного улучшить ситуацию, убрав часть клиентского JavaScript. К сожалению, технология пока ещё сырая и содержит много проблем. Я готов мириться с этим ради максимальной производительности, но главный блокер для меня — отсутствие поддержки CSS-in-JS. Как только это исправят (например, после выхода PigmentCSS), я перейду на RSC без раздумий.
Масштабирование
После разочаровывающих результатов на VPS я попробовал масштабировать свой Docker-контейнер (docker compose up -d --scale main=2 main
). Вместо одного контейнера с одним процессом Node.js, обрабатывающим все запросы, теперь у меня был по одному процессу на каждое ядро CPU (всего 2 ядра).

Примечание: Я использую
nginx-proxy
перед своими Docker-контейнерами. Это отличная штука: если несколько контейнеров слушают один и тот же домен, она автоматически балансирует нагрузку.
Эффект оказался минимальным. VPS теперь мог обрабатывать 275 запросов в секунду (+42,49%), но только 76% запросов укладывались в 500 мс. Этого всё ещё явно недостаточно, чтобы пережить настоящий «hug of death». А мне совсем не хочется, чтобы сайт лег.
Я также протестировал другие файлы, чтобы понять, как сервер справится с дополнительными 60+ запросами. Вот результаты:
Файл |
RPS |
Узкое место |
---|---|---|
Главная (20,3 КБ) |
275 RPS |
CPU |
Статья в блоге (13,9 КБ) |
440 RPS |
CPU |
Большой JS-чанк (46,8 КБ) |
203 RPS |
CPU |
Средний JS-чанк (6,1 КБ) |
833 RPS |
CPU |
Маленький JS-чанк (745 B) |
1 961 RPS |
CPU |
Изображение (17,7 КБ) |
1 425 RPS |
CPU |
Статически сгенерированный RSS-фид (2,4 КБ) |
849 RPS |
CPU |
Примечание:
Интересный момент: масштабирование дало лишь +42,49% прироста RPS на главной, потому что Next.js уже выполняет базовый многопоточный GZIP-компресс. То есть даже один контейнер немного использовал второе ядро CPU ещё до масштабирования.
Неожиданный всплеск трафика
Как назло, на следующий день после того, как я выяснил, что мой сервер вряд ли переживёт серьёзный «hug of death», моя статья Everything about Google Translate crashing React (and other web apps) попала на главную страницу Hacker News.

Это стало полной неожиданностью — я опубликовал статью два дня назад. Как оказалось, Hacker News может поднимать старые посты на главную, давая им второй шанс. Я был в восторге, что моя статья (над которой я долго работал) привлекла внимание, но тот факт, что это случилось сразу после того, как я понял, что сайт может не выдержать нагрузку, был особенно стрессовым.
К счастью, трафик оказался не таким высоким, как я ожидал от главной Hacker News, и мой VPS выдержал. Но это заставило меня задуматься: насколько разрушительным может быть настоящий «hug of death»? Эту тему я хочу разобрать в отдельной статье — с цифрами и статистикой по собственному опыту.
Поиск замены
Разочаровавшись в производительности VPS, я начал искать лучшее решение.
Cloudflare
Самый простой вариант — поставить Cloudflare перед моим сервером и заставить его агрессивно кэшировать статический контент. Я уже использовал такую схему: когда я запускал WoWAnalyzer, один сервер вместе с Cloudflare без труда обслуживал более 550 000 уникальных посетителей в месяц.
Звучит отлично: с Cloudflare моему серверу пришлось бы обрабатывать только первый запрос, а остальные 60 брала бы на себя сеть Cloudflare. Поскольку мой VPS уже выдерживает 200 посетителей в секунду, проблема была бы решена.
Но, к сожалению, для меня Cloudflare — не вариант. Я забочусь о вашей (и своей) конфиденциальности и не хочу, чтобы Cloudflare сидел между моими пользователями и сайтом, логировал каждый запрос и потенциально отслеживал людей по всему интернету.
Я хочу использовать исключительно сервисы, расположенные в ЕС.
Vercel
Ещё один очевидный вариант — Vercel (или его европейский аналог). Но честно говоря, для меня это звучит как кошмар. Это недёшево, а модель ценообразования «на сайт» означает, что я либо окажусь навсегда привязан к платформе, либо придётся разбирать всё при закрытии проекта. Плюс непредсказуемые лимиты и дополнительные платежи буквально за всё, за что только можно брать деньги. Это превращает «вирусный успех» из технической задачи в стресс о том, сколько придёт в следующем счёте (а это может быть более $100 000!). В моём нынешнем сетапе стоимость фиксированная — и это мне очень нравится.

Примечание: Я действительно плачу за Plausible, который тоже имеет тарификацию по использованию. Но здесь я не против платить по трём причинам: их ценовые ступени предсказуемы и разумны, они не берут доплаты за разовые всплески трафика, а сам сервис не критичен — я могу отказаться от него в любой момент. Возможно, в будущем попробую развернуть его локально, чтобы сэкономить, но в прошлый раз, когда я пытался сделать это с похожим продуктом аналитики, мне так и не удалось заставить его обрабатывать хотя бы часть трафика, который на него шёл.
Домашний сервер
Следующий вариант — домашний сервер, но для меня это не вариант. У меня нет оптики, чтобы обеспечить нормальное время отклика, я не хочу, чтобы мой IP был публичным, он ещё и динамический, а интернет и электричество не настолько надёжные, какими я хотел бы видеть их для сервера. Для вас это может быть отличным решением, особенно если критичность данных невысокая и вы готовы использовать Cloudflare.
VPS
Наверняка есть более быстрые VPS за те же деньги или даже дешевле, но я не хочу тратить время на исследование провайдеров, проверять, находятся ли они в ЕС, а потом платить только ради тестирования. Да и вряд ли другой VPS дал бы радикально лучшие результаты.
Выделенный сервер
С учётом всех этих ограничений выделенный сервер выглядит самым подходящим вариантом. Да, для этого сайта это немного перебор, но он ведь не единственный, что у меня есть. Мне нужно хорошее место для моих проектов, и я хочу, чтобы порог запуска нового был как можно ниже. Сервер, на котором я могу свободно экспериментировать, — это идеально. Он снимает барьеры для творчества.
К тому же полезно держать навык в тонусе.
Выделенный сервер
Много лет я арендовал выделенный сервер у So You Start, бюджетного бренда OVH, чтобы хостить WoWAnalyzer. В связке с Cloudflare этот сервер без проблем обслуживал более 550 000 уникальных посетителей в месяц, обрабатывая примерно 75 миллионов запросов и не напрягаясь. Поэтому я решил снова попробовать их.
Их серверы очень доступны по цене. Да, они используют более старое железо OVH (2+ лет), но соотношение цена/качество отличное. После быстрого сравнения я выбрал самый дешёвый вариант: 36 евро в месяц за Intel Xeon-E 2136 с 6 ядрами/12 потоками — всего на 8 евро дороже, чем стоил VPS.

Это обновление дало колоссальную разницу.
Запустив 12 инстансов (по одному на поток), сервер смог обрабатывать 2 330 запросов в секунду к главной странице (99% из них отвечали быстрее, чем за 500 мс).
Я также провёл серию тестов для других файлов, чтобы сравнить их производительность:
Файл |
RPS |
Разница |
Узкое место |
---|---|---|---|
Главная (20,3 КБ) |
2 330 RPS |
+747,27% |
CPU |
Статья в блоге (13,9 КБ) |
3 950 RPS |
+797,73% |
CPU |
Большой JS-чанк (46,8 КБ) |
1 249 RPS |
+515,27% |
Сеть |
Средний JS-чанк (6,1 КБ) |
7 627 RPS |
+815,61% |
CPU |
Маленький JS-чанк (730 B) |
16 175 RPS |
+724,83% |
CPU |
Изображение (17,7 КБ) |
3 216 RPS |
+125,68% |
Сеть |
Статически сгенерированный RSS-фид (2,4 КБ) |
9 395 RPS |
+1006,60% |
CPU |
На этот раз некоторые файлы упёрлись в сетевые ограничения. У бюджетных провайдеров, таких как So You Start, обычно ограничена пропускная способность — в данном случае максимум 500 Мбит/с.
Тем не менее, этого более чем достаточно для любых реальных нагрузок.
Средний размер файла примерно равен «среднему JS-чанку» (6,1 КБ). Значит, сервер способен обрабатывать около 7 600 запросов в секунду. Если разделить это на 60+ запросов, необходимых для загрузки главной страницы, получаем стабильный поток более 125 посетителей в секунду.
А статьи в блоге требуют меньше запросов (меньше изображений), так что на практике сервер выдержит ещё больше.
Это конфигурация, на которую я могу положиться.
Производительность SSR
Теперь, когда у нас есть хорошая база для сравнения производительности статического сайта, пора ответить на другой важный вопрос: как серверный рендеринг (SSR) сравнивается с предрендерингом?
Сложность тестирования SSR в том, что нужно решить, что именно измерять. SSR-страницы часто обращаются к базе данных (например, чтобы увеличить счётчик просмотров), вызывают API (например, поиск по Algolia) или обрабатывают пагинацию. Если учитывать это, тест будет измерять в основном накладные расходы этих интеграций, а не сам SSR.
Чтобы дать SSR лучший шанс, я сосредоточился только на том, что происходит при рендеринге React-страницы с небольшой синхронной логикой на сервере — без дополнительных API или базы данных.
Каждый тестовый кейс немного отличался:
Главная просто рендерит дерево компонентов React на сервере.
Статья в блоге делает немного логики (вычисляет связанные статьи) перед рендерингом.
RSS-фид генерируется динамически с помощью библиотеки
rss
.
SSR может по-разному влиять на эти кейсы, поэтому я ожидал интересных различий.
Результаты:
Файл |
RPS |
Разница |
Узкое место |
---|---|---|---|
Главная (20,3 КБ) |
271 RPS |
+88,37% |
CPU |
Статья в блоге (13,9 КБ) |
884 RPS |
+77,62% |
CPU |
Статически сгенерированный RSS-фид (2,3 КБ) |
4 521 RPS |
+51,88% |
CPU |
Честно говоря, мне понадобилось несколько минут, чтобы переварить эти цифры.
На первый взгляд 271 RPS на главной не кажется катастрофой. Особенно учитывая, что статические ассеты (CSS, JS, картинки) всё так же отдаются статически, и их RPS почти не меняется.
Но если подумать, то это всё, что способен выдать выделенный сервер, на котором больше ничего не крутится. Это почти на 90% меньше запросов в секунду, чем при статической генерации. И, что хуже всего, лишь 74% запросов к главной укладывались в 500 мс — медианное время ответа составило 1,51 секунды, а каждый десятый запрос занимал более 3 секунд.
SSR катастрофически медленный.
Для полноты картины я запустил те же тесты на VPS:
Файл |
RPS |
Разница |
Узкое место |
---|---|---|---|
Главная (20,3 КБ) |
34 RPS |
+87,64% |
CPU |
Статья в блоге (13,9 КБ) |
95 RPS |
+78,41% |
CPU |
Статически сгенерированный RSS-фид (2,3 КБ) |
490 RPS |
+42,29% |
CPU |
Ай.
Цифры ясно показывают: использовать SSR на странице, от которой вы ждёте большой трафик — это просто приглашение к проблемам.
Заключительные мысли
Всё началось с простого вопроса: выдержит ли мой сайт резкий всплеск трафика? Я всегда считал, что предрендеринг справится с чем угодно, но рад, что проверил это на практике. Оказалось, мой первоначальный VPS был далеко не таким надёжным, как я думал — всего три посетителя в секунду могли бы довести его до предела. Совсем не то, что я ожидал.
Выделенный сервер оказался настоящим апгрейдом по сравнению с VPS. И хотя стоит он ненамного дороже, ограничение по пропускной способности может стать скрытым узким местом. Нет смысла платить за более мощный CPU, если сеть не успевает за сервером.
Сейчас я думаю, что моя конфигурация способна выдержать больше, чем мне когда-либо реально понадобится.
Хотя, если быть честным, это всё ещё лишь предположение. Я всё ещё не знаю наверняка, насколько разрушительным может быть настоящий «hug of death». Но это тема для отдельной статьи — там же я поделюсь статистикой со своего выхода на главную Hacker News.
Что касается SSR, теперь у нас есть чёткие цифры, подтверждающие: предрендеринг масштабируется значительно лучше.
Мне всё ещё любопытно, насколько дальше можно разогнать сервер — без Next.js. В одной из следующих статей я проверю, изменится ли ситуация, если заменить сервер Next.js на Nginx и оптимизировать сжатие. Это покажет, насколько сам сервер Next.js эффективен.
Приложение
Изначально я планировал подробно рассказать обо всех инструментах, которые использовал, и обо всех результатах тестов, но статья и так получилась слишком длинной (мне пришлось разбить её на три части), поэтому я сократил этот раздел.
Скрипт k6:
import http from 'k6/http'
import { check } from 'k6'
const url = 'https://example.com'
// VUs are set to the amount needed to achieve 99% on the <500ms response time
// with a minimum of 200 to simulate a realistic peak amount of users.
// More VUs does not always mean more RPS, but it can lead to slower responses.
const vus = 200
export const options = {
// Skip decompression to greatly reduce CPU usage. This prevents throttling on
// my i9 MBP, making tests more reliable.
discardResponseBodies: true,
scenarios: {
rps: {
executor: 'ramping-vus',
stages: [
// A quick ramp up to max VUs to simulate a sudden spike in traffic.
// "constant-vus" leads to connection errors (DDOS protection?), so this
// ramp up is a bit more robust and realistic.
{ duration: '5s', target: vus },
{ duration: '1m', target: vus },
],
// Interrupt tests running longer than 1sec at the end of the test so that
// they don't skew the results.
gracefulStop: '1s',
},
},
}
export default function () {
const res = http.get(url, {
headers: {
'Accept-Encoding': 'br, gzip',
accept: 'image/webp',
},
})
check(res, {
'status is 200': (r) => r.status === 200,
'response time is below 500ms': (r) => r.timings.duration < 500,
})
}
Среда тестирования и наблюдения
Тесты проводились на топовой версии Intel MacBook Pro 2019 года, подключённом через Ethernet (USB-C адаптер), со скоростью загрузки ~760 Мбит/с, что выше пропускной способности выделенного сервера.
Сетевые адаптеры могут стать узким местом при бенчмарках — я убедился, что мой адаптер не ограничивает производительность.
Между тестами я давал ноутбуку остыть, чтобы свести влияние троттлинга к минимуму (скорее всего, оно было незначительным).
Большинство тестов я запускал минимум по три раза (а скорее всего, больше) из-за проблем с тестированием или изменений скрипта. Результаты оказались довольно стабильными.
Я сосредоточился на ответах <500 мс, потому что это примерно максимум терпения, который у меня есть, когда я открываю новый сайт впервые.
OVH заявляет, что «burst доступен для поглощения разовых пиков трафика», но я не заметил никаких признаков этой функции.
Я делал тесты сжатия и с Nginx, и результаты были интересные (но не такие значительные, как можно подумать) — поделюсь ими отдельно.
Я также исследовал возможный масштаб «hug of death» с Hacker News и Reddit, но вырезал этот раздел, чтобы статья не разрослась ещё больше. Вернусь к этому позже (надеюсь, в следующей статье, но не обещаю).
Тестируемые файлы
Я тестировал следующие файлы и URL. Хэши могли измениться, но аналогичные файлы можно найти на сайте:
Главная: https://next.martijnhols.nl/
Статья в блоге: The European Accessibility Act for websites and apps:
https://next.martijnhols.nl/blog/the-european-accessibility-act-for-websites-and-appsБольшой JS-чанк:
https://next.martijnhols.nl/_next/static/chunks/main-3e0600cd4aa10073.jsСредний JS-чанк:
https://next.martijnhols.nl/_next/static/chunks/102-38160eae18c28218.jsМаленький JS-чанк:
https://next.martijnhols.nl/_next/static/tKviHia3kWxxoRr-Rb42G/_ssgManifest.jsСтатически сгенерированный RSS-фид:
https://next.martijnhols.nl/rss.xml
Пожалуйста, не используйте эти URL для своих нагрузочных тестов.
Русскоязычное JavaScript сообщество

Друзья! Эту статью перевела команда «JavaScript for Devs» — сообщества, где мы делимся практическими кейсами, инструментами для разработчиков и свежими новостями из мира Frontend. Подписывайтесь, чтобы быть в курсе и ничего не упустить!
Комментарии (12)
proptech
26.09.2025 16:26Ради интереса запустил
ab
тест на машине 2 ядра 4 гига, чистое node.js + express приложение и чистый nginx. Все уперлось в возможности машины для запускаab
. Результат ~2000 RPS. Есть у кого возможность запустить нормально на двух машинах?ab -c 100 -n 50000 http://189.12.101.46/ -- для node.
Concurrency Level: 100 Time taken for tests: 24.093 seconds Complete requests: 50000 Failed requests: 0 Total transferred: 8850000 bytes HTML transferred: 100000 bytes Requests per second: 2075.30 [#/sec] (mean) Time per request: 48.186 [ms] (mean) Time per request: 0.482 [ms] (mean, across all concurrent requests) Transfer rate: 358.72 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 23 168.3 1 8112 Processing: 1 25 17.0 27 847 Waiting: 1 25 15.4 27 824 Total: 2 48 167.7 28 8136 Percentage of the requests served within a certain time (ms) 50% 28 66% 33 75% 35 80% 36 90% 40 95% 53 98% 233 99% 1030 100% 8136 (longest request)
ab -c 100 -n 50000 http://189.12.101.46/ -- для nginx.
Concurrency Level: 100 Time taken for tests: 24.914 seconds Complete requests: 50000 Failed requests: 0 Total transferred: 7900000 bytes HTML transferred: 100000 bytes Requests per second: 2006.92 [#/sec] (mean) Time per request: 49.828 [ms] (mean) Time per request: 0.498 [ms] (mean, across all concurrent requests) Transfer rate: 309.66 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 37 317.6 2 9740 Processing: 1 7 77.7 2 7522 Waiting: 1 7 76.3 2 7522 Total: 2 44 340.1 3 9839 Percentage of the requests served within a certain time (ms) 50% 3 66% 4 75% 4 80% 4 90% 6 95% 7 98% 1007 99% 1031 100% 9839 (longest request)
Nginx
server { listen 80; server_name localhost; location / { return 200 "OK"; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
server.js
const express = require('express') const app = express() app.disable('x-powered-by') app.get('/', (req, res) => res.send('ok')) app.listen(80)
Kerman
26.09.2025 16:26Я посмотрел на результаты исследований и ничего не понял. Как у Вас получилось получить 34 RPS для 20кб файла?
Я, может быть, в другой вселенной живу, но я не понимаю, как можно так затормозить, например kestrel, чтобы на VPS было 34RPS. Это дикость какая-то.
Tsimur_S
26.09.2025 16:26Без понятия что такое kestrel, гугл говорит что это что-то типо сервера для .net. Но 34 rps это про ssr, там по сути подымается движок браузера в нем из кода генерится html, отправляется клиенту и инжектится прямо строкой, ну или типа того. Не совсем тоже самое что просто отдать 20кб файл. А если взглянуть на сайт автора то и непонятно нахрена там вообще реакт.
Vlad_IT
26.09.2025 16:26там по сути подымается движок браузера
Не, там поднимается V8, среди сред интерпретируемых языков он очень шустр. Просто ему нужно выполнить все дерево компонентов, получить VDOM дерево реакта и снова по нему рекурсивной функцией пройтись и получить html. Я не понимаю почему, но renderToString написан ужасно не оптимально. Можно было сделать хитрее и сразу html составлять по вызову createElement, вместо чем потом снова рекурсивно обходить дерево.
На клиент, если это SSR классический, идёт потоковый html, который в нескольких event loop вставляется на страницу, это гораздо быстрее и эффективнее, чем составлять DOM дерево на клиенте или делать innerHTML. Идея очень крутая, но реализация хромает.
DmitryOlkhovoi
26.09.2025 16:26Мне всё ещё любопытно, насколько дальше можно разогнать сервер — без Next.js
Ну вы же понимаете js использует commonjs который по сути реализован за счёт движка написанном на C++. Если хочется экстрима, вспомните как PHP расширяют сишными либами.
Так же стоит рассмотреть bun, deno
И конечно в реале, есть cnd кеширование, балансеры и так далее)
Скорее вы возьмете другой стек под специфику, чем упретесь в производительность ноды.
izibrizi2
26.09.2025 16:26Классические текстовые шаблоны имеют крайне ограниченный контекст, поэтому они зачастую преобразуются в нативный код языка, в котором они обрабатываются, с последующим скармливанием в jit. А у реакт компонентов, помимо шаблона, есть еще куча вещей, которые надо обработать - стейт, хуки, события, виртуальный дом. Нас же интересует гидрация, с последующим продолжением работы на клиенте, поэтому - извините. Это всё несказанно медленнее классических текстовых шаблонов. С другой стороны, идея ssr как раз таки расчитывает на гидрацию, после которой рендерить компоненты клиентская сторона, разгружая сервер, н, как это принято в реакт сообществе - что-то пршло не так и гачали вводить серверные компоненты, которые противоречат изначальной идее ssr (было бы логичнее делать серверный рендер только для поисковиков, а для человека - просто отдать скрипты приложения). Ну и в целом, реакт и некст жс очень тяжелые, есть смысл попробовать ssr на vue, svelte, solidjs - там всё гораздо быстрее и проще. А цифры в статье - это курам на смех, попахивает пхп и 2000 годом :)
omyraucy
26.09.2025 16:26Не понимаю, зачем так усложнять. Почему нельзя развернуть сервис на ванильной js, сделать load balancer и прикрепить к autoscaling group на aws? Будет эффективнее vds.
Vlad_IT
Часто в SSR спасает кэширование. Можно кешировать SSR результат допустим для неавторизированных пользователей. Для них вариативность ответа часто мала. Плюс можно ключ кеша сформировать для разных групп пользователей.
Но вообще, renderToString даже базовый, ужасно медленный, там легко рендер будет выполняться 20-40мс. Плюс ещё много чего лишнего выполняется на сервере, что нужно только на клиенте, типа чтения пропсов событий, создание массива зависимостей для хуков. Ещё, например, makeObservable от mobx совершенно не нужен на клиенте, но создаёт лишние вычисления. Поэтому нужно брать профайлер в зубы и смотреть, может там какая-то функция формирования урлов ссылок или картинок кучу вычислений делает.
Ещё в идеале нужен инструмент, который бы позволял настраивать кеш верстки для отдельных компонентов. Условная шапка или футер не особо вариативны, поэтому можно высчитать строку один раз (такую библиотеку видел, но она не особо развивается).
Но в целом, конкатенация строк в js не особо быстрая, даже через шаблонизатор не сильно шустрее будет работать. Все хочу попробовать сделать нативный модуль renderToString на С или Rust, но пока руки не доходили.
Плюс я так и не понял, у автора NodeJS отдает статику js/css? Это должен делать nginx или аналоги. Плюс судя по проблемам с сетью, у него не настроен gzip, но это мои предположения.
markelov69
А для авторизованных он как бы вообще не нужен, от слова совсем. Для авторизованных просто режим SPA (Client Side Rendering). Более того и Next.js вообще нафиг не нужен для SSR. Это задача решается элементарно, один раз в жизни пишется сервис который на вход получает набор урлов потом будет puppeteer ходить и класть результат в кэш, а при запросах отдавать ответ напрямую из кэша. Никаких ограничений и костылей next.js'a, гигантская производительность ибо все отдается из кэша и одна виртуалка за 5$ обслужит много тысяч RPS.
BugsSearchBot
Если у вас статические данные, а если это биржа, обменник , аукцион??
markelov69
Ну обычно раз в 5 минут и так в фоне все страницы обновляются, поэтому они "устаревают" в среднем на 5 минут при самом простом исполнении. Но если нужно реагировать быстро, то опять же не проблема, webhook/rabbitmq и т.п. нотификация о том, что надо обновить кэш для такого-то урла и тогда задержка условно в пару секунд, что для 99.99999% случаем более чем достаточно
Так при открытии страницы на блоках с катировками лоадер "инитного получения данных", а дальше работает как обычно websocket и т.п. и обновляется в real time.
DmitryOlkhovoi
Ну если брать чисто next.js то он даже картинки кеширует на беке, оптимизируя разрешение и прочее, как вам нужно и полностью сервит статику. Если у вас изоморфное app, то nginx обычно в роли балансера. Это уже нужно предметно по задаче обсуждать. Понятное дело статья про сыерического коня в вакууме.
По поводу отдельных компонентов, это уже микрофронты.
Сейчас еще мода на htmx, alpine и старые добрые шаблоны без js рендера на беке. Там и cnd кешить может угу.
Фронтовые стеки сейчас дикий зоопарк, как мем с синим квадратом