13 июн. 2025 г.·7 мин чтения

Ключи кэша Cloudflare для SaaS-приложений с безопасными вариантами

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

Ключи кэша Cloudflare для SaaS-приложений с безопасными вариантами

Почему один вариант кэша ломает страницы SaaS

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

SaaS-приложения перестают быть такими простыми сразу после входа пользователя в систему. Теперь страница зависит от клиента, от данных этого клиента и от прав текущего пользователя. Если Cloudflare сохранит один ответ и будет переиспользовать его для всех, он может отдать не ту страницу следующему запросу.

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

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

Feature flags добавляют ещё один слой. У одного клиента может быть включён новый отчёт, а у другого всё ещё старая версия. Бета-пользователь может видеть дополнительную панель. Сотрудник может видеть переключатель эксперимента, скрытый от всех остальных. Если кэш не разделяет такие случаи, одна версия начнёт просачиваться в другую.

Именно поэтому ключ кэша для SaaS должен учитывать не только URL. Маршрут может оставаться тем же, а корректный ответ меняться в зависимости от аудитории. На практике большинство страниц приложения попадает в три группы: публичные страницы, которыми можно делиться, клиентские страницы, которые должны оставаться внутри одного аккаунта, и операторские страницы, которым нужна ещё более жёсткая изоляция.

Если все эти варианты делят одну запись кэша, скорость на мгновение растёт, а корректность исчезает. Обычно такая сделка того не стоит.

Найдите, что именно меняется

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

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

Затем отметьте страницы, которые действительно меняются. В большинстве SaaS-приложений причины обычно простые: клиент меняет содержимое или брендинг, роль пользователя меняет доступные действия, feature flag включает или выключает целый раздел, либо на странице есть личные или аккаунтные данные.

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

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

Простой дашборд хорошо показывает идею. Допустим, у всех клиентов одинаковая рамка, но у каждого клиента свой логотип и баннер с тарифом. У операторов поддержки есть ещё одна панель, а один beta-флаг добавляет новый график использования. Это всё равно не значит, что вам нужны десятки версий. Может хватить всего трёх корзин — публичной, клиентской и операторской — плюс одной дополнительной версии, если beta-график меняет весь HTML.

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

Используйте три группы ответов

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

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

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

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

Хорошая настройка обычно выглядит так:

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

Порядок правил важен. Сначала матчьте операторский трафик или изолируйте его на отдельном hostname или path. Если операторские запросы смогут просочиться через широкое клиентское правило, вся схема очень быстро станет небезопасной.

Такое разделение ещё и упрощает отладку. Когда страница выглядит неправильно, сначала можно задать один вопрос: это был публичный, клиентский или операторский контент?

Добавляйте feature flags без убийства hit rate

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

Лучше сводить флаги к небольшому bucket'у страницы. Этот bucket должен отражать только то, что меняет HTML, который возвращает сервер. Если у двух пользователей разные сырые флаги, но на выходе одинаковая разметка, они должны делить один кэшированный ответ.

Страница биллинга — хороший пример. У одного клиента могут быть new_invoice_table=true, compact_cards=false и tooltip_refresh=true. У другого — другая комбинация. Но если оба клиента всё равно получают от сервера один и тот же invoice layout, они должны попасть в один кэш-бакет.

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

Не включайте флаги, которые влияют только на поведение после загрузки страницы. Задержка tooltip, анимация графика или состояние свернутой панели не должны дробить кэш. Такие детали относятся к коду браузера, а не к логике вариантов кэша.

Ещё помогает группировать связанные эксперименты. Пять флагов для биллинга не требуют пяти измерений кэша, если все они поддерживают одно более крупное изменение. Сопоставляйте их с одним bucket'ом, например billing-v2, вместо того чтобы добавлять имя и значение каждого флага отдельно.

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

Если держать комбинации маленькими, кэш остаётся полезным. Для многих SaaS-страниц хватает базового bucket'а, одного-двух feature-бакетов и операторского bucket'а.

Собирайте ключ кэша по шагам

Упорядочьте варианты флагов
Превратите запутанные feature flags в небольшое число кэш-бакетов, которые не ломают страницы.

Начинайте с малого. Хороший ключ кэша включает только те входные данные, которые реально меняют HTML. Если добавить каждый header, cookie и query-параметр, hit rate рухнет, а отладка станет мучительной.

Начните с пути. Для многих страниц /dashboard и /settings уже достаточно хорошо разделяют содержимое. Потом проверьте query string и оставьте только параметры, которые меняют то, что видит пользователь. Порядок сортировки таблицы может иметь значение. Трекерский тег — обычно нет.

Практический порядок сборки выглядит так:

  1. Добавьте путь.
  2. Оставьте только query-параметры, которые меняют содержимое.
  3. Добавьте идентификатор клиента, если страница показывает клиентские данные.
  4. Добавьте ролевую группу вроде public, tenant или operator.
  5. Добавьте один небольшой feature bucket только в том случае, если он меняет всю страницу.

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

Ролевую часть лучше оставлять грубой. Не используйте полный набор прав. Группируйте пользователей в несколько типов ответа, которые реально меняют форму страницы. В большинстве SaaS-приложений достаточно public, tenant и operator.

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

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

Безопасный результат может выглядеть так: /dashboard + tenant_42 + tenant + flags_A. Коротко, читаемо и удобно для тестов.

Простой пример с дашбордом

Представьте один маршрут: /dashboard/billing.

Посетитель, который ещё не вошёл в систему, приходит туда со страницы цен и видит общий обзор тарифа, таблицу функций и приглашение начать trial. Эту страницу можно отдавать из одной публичной кэш-записи для всех, потому что на ней ничего не зависит от данных клиента, состояния аккаунта или staff tools.

Теперь переключимся на Acme, реального клиента. Когда пользователь Acme открывает тот же маршрут, страница показывает billing summary Acme: текущий тариф, количество мест, дату следующего счёта и то, привязан ли способ оплаты. Этот ответ не должен делить кэш с публичной страницей и не должен делить кэш с любым другим клиентом.

Маршрут остаётся тем же, но ключ кэша меняется в зависимости от группы ответа.

Что кэшируется

Для этой одной страницы обычно получается небольшой набор вариантов:

  • одна публичная запись для анонимных посетителей
  • одна клиентская запись для пользователей Acme на обычной странице биллинга
  • ещё одна клиентская запись для Acme, когда включён beta-флаг биллинга
  • одна операторская запись, когда support-сотрудники открывают тот же маршрут со своей панелью инструментов

Так вы получаете быстрые ответы без смешивания аудиторий.

Beta-флаг важен, потому что он меняет layout страницы или данные, которые на ней показаны. Если у Acme включён billing_v2, Acme нужна отдельная кэшированная версия для этого состояния флага. Но нельзя раздувать количество вариантов, добавляя в ключ каждый флаг в системе. Включайте только те флаги, которые меняют HTML на этой странице.

Support-сотрудникам тоже нужен свой вариант. Операторская панель может показывать внутренние действия, заметки или элементы управления аккаунтом. Если такой HTML разделит кэш с обычными пользователями, кто-то рано или поздно увидит кнопку, которой у него не должно быть.

Проще говоря, разделение выглядит так:

/dashboard/billing + public

/dashboard/billing + tenant=acme + billing=v1

/dashboard/billing + tenant=acme + billing=v2

/dashboard/billing + tenant=acme + operator

Сделайте то же самое для каждого клиента. Acme получает кэшированную страницу Acme. BrickCo получает кэшированную страницу BrickCo. Вы сохраняете хороший hit rate и избегаете худшей ошибки SaaS-кэширования: быстрой страницы с чужими данными.

Ошибки, которые приводят к утечке чужого контента

Защитите операторские экраны
Разделите операторские страницы и клиентский трафик до того, как пользователи увидят лишнюю разметку.

Команды обычно ломают кэширование небольшими, обычными способами. Страница выглядит правильно в одной тестовой сессии, а потом клиент открывает тот же маршрут и видит неправильный баннер, неправильную кнопку или control только для админа.

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

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

Cookies и query-параметры тоже ловят людей. Если вы меняете ответ по каждому cookie, Cloudflare считает крошечные различия сессий разными страницами. Если вы меняете ответ по каждой query string, безобидные параметры вроде utm_source, ref или небольших значений сортировки могут раздуть количество вариантов. Хорошие ключи кэша остаются избирательными. Они включают только то, что меняет отрендеренный ответ.

Feature flags создают медленный дрейф, когда команды оставляют старые флаги в продакшене. Флаг может больше не иметь значения в коде, но правило кэша всё ещё читает его и продолжает дробить ответы. Через несколько месяцев никто уже не помнит, почему два пользователя получают разный HTML. Удаляйте мёртвые флаги и из приложения, и из логики кэша.

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

Быстрые проверки перед запуском

Спланируйте безопасный запуск
Проверьте план запуска по маршрутам, прежде чем менять продакшен-трафик.

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

Начните с одного и того же URL в трёх состояниях: публичном, клиентском и операторском. Используйте отдельные профили браузера или приватные окна, чтобы cookies не смешивались. Загрузите каждую версию дважды. Первый запрос может прогреть кэш, а второй должен показать, что правила разделяют ответы так, как вы ожидаете.

Затем переключитесь между клиентами, сохранив ту же роль. Пользователь из Клиента A никогда не должен видеть брендинг, счётчики, billing-информацию или названия проектов Клиента B. Это звучит очевидно, но утечки между клиентами часто происходят из-за одного забытого входа в ключ кэша, например отсутствующего идентификатора клиента или cookie-правила, которое слишком много трафика складывает вместе.

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

Короткий тест-проход ловит большинство ошибок запуска:

  • Откройте одну страницу как публичный пользователь, клиент и оператор.
  • Переключитесь между двумя клиентами с одной и той же ролью.
  • Включите и выключите один feature flag.
  • Перезагрузите каждый вид дважды и сравните, что меняется.
  • Проверьте headers, origin logs и cache status для каждого запроса.

После этого понаблюдайте за метриками ещё какое-то время. У общих страниц hit rate должен стать лучше. Клиентские страницы могут попадать в кэш реже, но при повторяющемся трафике они всё равно не должны порождать шквал запросов к origin.

Если страница постоянно мимо кэша, посмотрите, что меняется при каждом запросе. Частые виновники — полные cookies, случайные query-параметры, request headers без практической ценности и timestamps, встроенные в путь ответа.

Если вам нужен второй взгляд перед запуском, именно такой архитектурный аудит Oleg Sotnikov часто делает через oleg.is в роли fractional CTO: практическая проверка правил кэша, границ клиентов и логики вариантов до того, как маленькие ошибки превратятся в обращения в поддержку.

Следующие шаги для безопасного запуска

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

Запишите точные входные данные для каждого ключа кэша до того, как трогать production. Делайте описание простым: путь, идентификатор клиента, группа роли и небольшой набор feature flags, которые действительно меняют ответ. Если флаг меняет только цвет кнопки или tooltip, скорее всего, ему не место в ключе.

Следите за скоростью и корректностью одновременно. Более быстрая страница не помогает, если один клиент видит версию другого клиента или если операторский вид попадает в обычную пользовательскую страницу. Смотрите на hit rate кэша, time to first byte и простой счётчик неправильных страниц из обращений в поддержку, QA-проверок и server logs.

Краткий чек-лист запуска помогает:

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

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

Если роли, клиенты и флаги пересекаются запутанным образом, лучше ещё раз посмотреть на план перед расширением запуска. Это архитектурная проверка, а не косметическая. Опытный fractional CTO обычно быстро замечает, где правило кэша выглядит безопасным на бумаге, но ломается при смешанном трафике.

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

Часто задаваемые вопросы

Что нужно включить в ключ кэша для SaaS?

Начните с пути, а затем оставьте только те query-параметры, которые меняют HTML. Добавьте идентификатор клиента для клиентских данных, крупную ролевую группу вроде public, tenant или operator, и один небольшой bucket для feature flags только тогда, когда он меняет всю страницу.

Уберите всё, что не влияет на ответ сервера. Короткий и понятный ключ кэша проще тестировать, и он лучше переиспользуется.

Нужно ли включать user ID в ключ кэша?

Обычно нет. User ID создаёт слишком много вариантов и убивает переиспользование кэша.

Если страница меняется только из-за клиента, роли или одного небольшого bucket'а флагов, кэшируйте именно эту общую версию. Личные части, например приветствие, недавнюю активность или черновики, загружайте отдельным запросом.

Как разделить публичные, клиентские и операторские страницы?

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

Операторский трафик лучше матчинить первым или вынести на отдельный путь либо hostname. Так вы не смешаете support и admin-экраны с клиентскими кэш-записями.

Когда feature flags должны менять ключ кэша?

Добавляйте флаг в ключ кэша только тогда, когда он меняет HTML, который отдаёт сервер. Если флаг меняет layout страницы, навигацию, блоки данных или staff controls, включайте его.

Если он меняет только tooltip, анимацию или свернутое состояние после загрузки, не добавляйте его. Связывайте похожие флаги в один bucket, например billing-v2, вместо того чтобы хранить каждое сырое значение отдельно.

Можно ли безопасно кэшировать страницы дашборда?

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

Так страницы будут открываться быстрее, и при этом в общий HTML не попадут чужая активность, черновики или счётчики.

Почему операторским страницам нужен отдельный cache rule?

Потому что операторы часто видят дополнительные элементы управления, внутренние заметки, debug tools или impersonation actions. Даже если layout выглядит похожим, ответ не такой же, как у обычной клиентской страницы.

Если операторский трафик разделяет одну cache entry с клиентами, кто-то рано или поздно увидит разметку, которую ему нельзя показывать. Отдельные правила убирают этот риск заранее.

Какие query-параметры и cookies можно игнорировать?

Игнорируйте tracking tags вроде utm_source, referral-теги и большинство cookies. Они часто меняются, но не меняют содержимое страницы.

Оставляйте только те значения, которые действительно влияют на HTML, например фильтр, сортировку или locale. Если вы зависите от каждого cookie или каждого query-параметра, кэш быстро дробится.

Какие ошибки чаще всего приводят к утечке чужого контента?

Обычно команды забывают про tenant ID, смешивают операторский трафик с клиентским или оставляют в cache logic устаревшие feature flags. Ещё одна частая проблема — персонализированный фрагмент внутри страницы, которая в остальном общая.

Проблемы также появляются, когда вы учитываете каждый cookie или каждый query-параметр. Это усложняет отладку и снижает hit rate.

Как это тестировать перед запуском?

Откройте один и тот же URL в трёх состояниях: публичный, клиентский и операторский. Используйте разные профили браузера или приватные окна, чтобы cookies не смешивались, и загрузите каждую версию дважды, чтобы проверить кэшированный ответ.

Затем переключитесь между двумя клиентами с одной и той же ролью и включите и выключите один feature flag. Посмотрите cache status, headers и origin logs, чтобы точно понять, какой вариант использовал каждый запрос.

Какой план запуска лучше выбрать для более безопасного кэширования?

Начните с небольшой группы маршрутов, например с одной публичной страницы, одной клиентской страницы и одной операторской страницы. До изменений в production запишите все входные данные кэша.

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