Кеш перед серверним відтворенням: як зменшити затримку й CPU без повної статичної збірки

HTTP-кешуваннясерверне відтворенняпродуктивність

Робочий стіл із мапою публічних і приватних маршрутів для безпечного кешування серверних відповідей

Сценарій: оптимізуємо не фреймворк, а повторення

У команди є серверно відтворені сторінки: каталог, документація, маркетингові сторінки, публічні профілі або статті. Частина відповідей однакова для тисяч користувачів, але застосунок усе одно запускає код на кожен запит: читає дані, збирає HTML, виконує шаблони, віддає відповідь. Затримка росте, CPU витрачається, а команда починає думати про повну статичну збірку.

Інколи кращий перший крок простіший: поставити кеш перед кодом. Якщо готова публічна відповідь уже є і безпечна для повторного використання, її можна віддати з кешу. Якщо її немає, запит доходить до початкового сервера, код виконується один раз, а результат зберігається для наступних запитів.

Свіжий приклад такої моделі — Workers Cache від Cloudflare, де кеш може стояти перед Worker, який сам є початковим сервером для застосунку із серверним відтворенням. Але головний урок не в конкретному продукті. Урок у підході: кеш має стояти перед дорогим виконанням коду, а не тільки перед статичними файлами.

Крок 1. Інвентаризація маршрутів перед будь-якими заголовками

Не починайте з Cache-Control. Почніть із таблиці маршрутів. Для кожного маршруту запишіть, хто бачить відповідь, від чого вона залежить і як швидко має оновлюватися.

Практичний поділ:

  • публічна: сторінка однакова для всіх, наприклад документація або публічна стаття;
  • умовно публічна: відповідь залежить від мови, країни, валюти або типу пристрою;
  • приватна: відповідь залежить від користувача, авторизації, файлів cookie, ролі або сесії.

Приватні відповіді не повинні потрапляти в спільний кеш. Якщо сторінка містить ім’я користувача, особисті замовлення, стан підписки або інші персональні дані, ставте Cache-Control: no-store і перевіряйте, що платформа не може закешувати таку відповідь через загальні правила.

Крок 2. Заголовки: Cache-Control і Vary як контракт

Для публічного маршруту заголовок може бути коротким і зрозумілим, наприклад Cache-Control: public, max-age=60, stale-while-revalidate=300. Це означає: відповідь можна зберігати, вона свіжа протягом 60 секунд, а після цього кеш ще може швидко віддати стару версію під час фонового оновлення.

Але Cache-Control — лише половина контракту. Друга половина — Vary. Якщо сторінка має різні мовні версії, кеш повинен знати, що Accept-Language впливає на відповідь. Якщо відповідь стиснена по-різному, часто враховується Accept-Encoding. Якщо ви раптом хочете варіювати відповідь за Cookie, це сигнал зупинитися: можливо, маршрут насправді приватний і не має бути у спільному кеші.

Окремо перевірте Set-Cookie. Відповідь, яка встановлює файл cookie, часто не повинна кешуватися як публічна. Типова помилка — публічна сторінка додає аналітичний або сесійний cookie, після чого команда намагається кешувати її без перегляду заголовків.

Крок 3. stale-while-revalidate: швидкість із межами

stale-while-revalidate корисний для сторінок, де невелика затримка оновлення прийнятна. Наприклад, документація, блог, довідкові сторінки або каталог без критичних залишків можуть витримати кілька хвилин несвіжості. Користувач отримує швидку відповідь, а кеш оновлюється у фоні.

Це поганий вибір для даних, де застаріла відповідь шкодить: ціна під час оплати, статус доступу, баланс, персональні повідомлення, правові обмеження. Для таких маршрутів або не використовуйте кеш, або робіть дуже вузькі правила з явним очищенням кешу після зміни даних.

Домовтеся про допустимий час несвіжості: скільки секунд або хвилин сторінка може бути застарілою без інциденту. Якщо команда не може відповісти, значення stale-while-revalidate поки не готове.

Крок 4. Очищення кешу: не кнопка паніки, а мапа контенту

Кеш без стратегії очищення швидко стає джерелом страху. Потрібна мапа: яка зміна контенту очищує які відповіді.

Приклади:

  • публікація статті очищує її шлях і список статей;
  • зміна товару очищує сторінку товару, категорію і пошукові фрагменти;
  • оновлення навігації очищує всі сторінки з цим меню або відповідний Cache-Tag;
  • зміна перекладу очищує тільки мовну версію, а не весь сайт.

Cloudflare у своєму матеріалі згадує Cache-Tag і програмне очищення кешу як частину моделі Workers Cache. У будь-якій платформі думайте так само: очищення за тегом або префіксом має бути достатньо точним. Антипатерн — очищувати весь кеш після кожної маленької зміни. Це повертає навантаження на початковий сервер і маскує проблеми в мапі залежностей.

Крок 5. Короткі перевірки у тестовому середовищі

Перед впровадженням у робочому середовищі зробіть короткий набір перевірок.

  1. Відкрийте публічний маршрут двічі. Перший запит має показати промах кешу, другий — влучання кешу.
  2. Перевірте журнали або метрику CPU: під час влучання кешу код застосунку не має виконувати повну генерацію відповіді.
  3. Повторіть тест для різних мов або інших значень Vary. Версії не повинні змішуватися.
  4. Перевірте заголовки відповіді: немає Set-Cookie, немає приватних даних, немає залежності від Authorization.
  5. Оновіть контент, виконайте очищення кешу за шляхом або тегом, переконайтеся, що наступний запит бачить нову версію.
  6. Тимчасово вимкніть кеш або правило маршруту й перевірте, що запити коректно йдуть до початкового сервера.

Антипатерни, які варто зловити до релізу

Перший антипатерн — кешувати все, що повільне. Повільність не робить відповідь безпечною для повторного використання.

Другий — ігнорувати Vary. Якщо сторінка залежить від мови, але кеш цього не знає, користувачі отримають чужу мовну версію.

Третій — використовувати довгий max-age без очищення кешу. Це створює швидкий сайт, якому ніхто не довіряє.

Четвертий — тестувати тільки швидкість. Для серверно відтворених відповідей обов’язково тестуйте приватність, свіжість і те, чи справді влучання кешу не запускає дорогий код.

Обережне впровадження

Почніть з одного публічного маршруту з низьким ризиком: документація, довідкова сторінка або стаття. Додайте заголовки, Vary, очищення кешу та короткі перевірки. Потім розширюйте покриття на групи маршрутів з однаковими правилами.

Мета не в тому, щоб перетворити весь застосунок на статичний сайт. Мета — прибрати зайве повторення там, де відповідь уже готова, публічна і контрольовано оновлюється.

Джерела

Короткий чеклист

  • складіть список маршрутів і позначте публічні, приватні та умовно публічні відповіді
  • забороніть кешування для відповідей з авторизацією, персональними даними або Set-Cookie
  • задайте Cache-Control і Vary для кожного маршруту, який можна кешувати
  • визначте, де доречний stale-while-revalidate і яка допустима несвіжість
  • підготуйте очищення кешу за шляхом, префіксом або тегом для подій оновлення контенту
  • перевірте у тестовому середовищі влучання, промах, свіжість і відсутність приватних даних

Скласти безпечний план кешування серверних маршрутів

Ти допомагаєш підготувати обережне впровадження кешу перед серверно відтвореним застосунком. Постав уточнювальні питання, якщо бракує даних. Попроси такі вхідні дані: - список маршрутів і приклади відповідей; - які маршрути залежать від авторизації, файлів cookie, мови, країни або пристрою; - допустиму затримку оновлення контенту для кожного типу сторінки; - як контент оновлюється у CMS, базі даних або адмінці; - які сигнали влучання та промаху кешу доступні у платформі; - як команда може очищувати кеш за шляхом, префіксом або тегом; - обмеження тестового й робочого середовищ. Поверни результат у такому форматі: 1. Матриця маршрутів: маршрут, публічність, можливість кешування, причина, ризик. 2. Рекомендовані заголовки: Cache-Control, Vary та правила Set-Cookie. 3. План stale-while-revalidate: значення, коли доречно, коли заборонено. 4. Мапа очищення кешу: подія зміни контенту, шлях або тег, очікуваний ефект. 5. Короткі перевірки: команди або кроки для влучання, промаху, свіжості та приватності. 6. План відкату: як вимкнути кеш і перевірити повернення до початкового сервера. Не пропонуй кешувати відповіді з персональними даними, авторизацією або Set-Cookie без окремого обґрунтування.