HTTP кеширование в REST API
Введение: холодильник и срок годности 🥛
Представь холодильник с пометками: что свежее, а что уже нельзя есть.
Кэширование — это такие же пометки для данных, чтобы не готовить одно и то же заново.
💡 Совет: если данные не меняются каждую секунду — их можно кэшировать.
✅ Вывод: кэш экономит время, деньги и нервы команды.
Проблема → решение
Проблема: без кэша сервер делает одну и ту же работу снова и снова.
Решение: добавить правила кэширования и объяснить клиентам, когда можно использовать старые данные.
✅ Вывод: кэширование снижает нагрузку и ускоряет API.
Чем помогает и как работает
Кэширование делает ответы быстрее и снижает число одинаковых запросов.
Если данные не изменились, сервер может вернуть 304 без тела.
Как это работает:
- Клиент получает ответ и хранит его.
- При повторе отправляет условный запрос.
- Сервер возвращает 304, если данных нет.
✅ Вывод: меньше трафика — быстрее работа API.
Ключевые термины (простыми словами)
- Cache (кэш) — сохранённый ответ, чтобы не запрашивать его снова.
- Cache-Control — правила, можно ли хранить и сколько.
- ETag — уникальная метка версии ответа.
- If-None-Match — запрос «если версия не изменилась».
- Last-Modified — время последнего изменения.
- If-Modified-Since — запрос «если не менялось после даты».
- 304 Not Modified — ответ, что изменений нет.
- Public/Private cache — общий или персональный кэш.
- Vary — зависит ли ответ от заголовков (язык, формат).
Самое важное (must‑know)
- Для кэша нужны понятные правила Cache-Control.
- 304 возвращается без тела, экономит трафик.
- Персональные данные кэшируются только как private или no-store.
- ETag помогает избежать лишних 200 ответов.
- Кэш нужно сбрасывать после обновлений.
✅ Вывод: это ключевые темы для интервью по HTTP кэшу.
1. Cache-Control — правила хранения
Назначение: сказать, можно ли и сколько хранить ответ.
Простыми словами: объясняем, сколько времени ответ считается свежим.
Аналогия: наклейка «годен 5 минут».
Пример:
Cache-Control: public, max-age=60🔎 Как это происходит на практике:
- Сервер отдаёт Cache-Control.
- Клиент хранит ответ 60 секунд.
- Пока срок не истёк — повтор не нужен.
Характеристики:
- ✅ max-age задаёт срок жизни.
- ✅ no-store запрещает хранение.
- ✅ no-cache требует проверки.
Когда использовать: для публичных списков и часто читаемых данных.
✅ Вывод: Cache-Control — основа кэширования.
2. ETag и If-None-Match
Назначение: проверить, изменились ли данные.
Простыми словами: сверяем метку версии.
Аналогия: сравнить номер версии файла.
Пример:
ETag: "v3"If-None-Match: "v3"🔎 Как это происходит на практике:
- Клиент хранит ETag.
- Делает запрос с If-None-Match.
- Сервер возвращает 304, если версия та же.
Характеристики:
- ✅ Точный контроль версий.
- ✅ Экономия трафика.
- ✅ Хорош для детальных страниц.
Когда использовать: для карточек, профилей, деталей.
✅ Вывод: ETag снижает лишние ответы 200.
3. Last-Modified и If-Modified-Since
Назначение: проверять изменения по дате.
Простыми словами: спрашиваем «обновлялось ли после времени».
Аналогия: чек на товаре с датой производства.
Пример:
Last-Modified: Tue, 20 Feb 2025 10:00:00 GMTIf-Modified-Since: Tue, 20 Feb 2025 10:00:00 GMT🔎 Как это происходит на практике:
- Клиент запоминает дату изменения.
- Отправляет If-Modified-Since.
- Сервер отвечает 304, если изменений нет.
Характеристики:
- ✅ Проще, чем ETag.
- ✅ Может быть неточным при частых изменениях.
- ✅ Работает на уровне времени.
Когда использовать: когда не нужна точная версия.
✅ Вывод: Last-Modified — простой, но не идеальный.
4. Public и Private cache
Назначение: определить, можно ли делиться кэшем.
Простыми словами: можно ли хранить ответ для всех или только для одного.
Аналогия: личная тетрадь vs общая библиотека.
Пример:
Cache-Control: public, max-age=300Cache-Control: private, max-age=60🔎 Как это происходит на практике:
- Списки курсов = public.
- Профиль пользователя = private.
- Персональные данные не уходят в общий кэш.
Характеристики:
- ✅ public для общих данных.
- ✅ private для персональных.
- ✅ no-store для чувствительных данных.
Когда использовать: всегда, когда есть персонализация.
✅ Вывод: правильный тип кэша защищает данные.
5. Сброс кэша после изменений
Назначение: не отдавать устаревшие данные.
Простыми словами: если обновили — кэш должен забыть старое.
Аналогия: переписали расписание — старую бумагу выбросили.
Пример:
POST /courses → Cache-Control: no-store🔎 Как это происходит на практике:
- После POST/PUT/PATCH/DELETE сбрасываем кэш.
- Списки обновляются заново.
- Клиенты получают свежие данные.
Характеристики:
- ✅ Изменения не «залипают».
- ✅ Нет устаревших данных.
- ✅ Повышается доверие к API.
Когда использовать: всегда после мутаций.
✅ Вывод: без сброса кэш становится врагом.
6. Заголовок Vary
Назначение: учитывать разные версии ответа.
Простыми словами: кэш зависит от языка, формата и других заголовков.
Аналогия: меню на разных языках.
Пример:
Vary: Accept-Language🔎 Как это происходит на практике:
- Клиент запрашивает русский язык.
- Сервер кэширует русскую версию.
- Английская версия хранится отдельно.
Характеристики:
- ✅ Защищает от неправильного языка.
- ✅ Делает кэш корректным.
- ✅ Особенно важно для i18n.
Когда использовать: когда ответ зависит от заголовков.
✅ Вывод: Vary спасает от неверных данных.
Сравнение: ETag vs Last-Modified
| Подход | Плюс | Минус |
|---|---|---|
| ETag | Точная версия | Нужно хранить метку |
| Last-Modified | Просто и быстро | Может быть неточно |
Часто спрашивают на собеседованиях
- Чем отличается Cache-Control от Expires?
- Когда возвращается 304?
- Что лучше — ETag или Last-Modified?
- Как кэшировать персональные данные?
- Зачем нужен Vary?
✅ Вывод: эти вопросы показывают понимание кэша.
Типичные ошибки
Ошибка 1: Нет Cache-Control
❌ Неправильно: ответы без правил кэша.
✅ Правильно: всегда указывать Cache-Control.
Почему: иначе клиенты кэшируют как попало.
✅ Правильно: всегда указывать Cache-Control.
Почему: иначе клиенты кэшируют как попало.
Ошибка 2: Кэш персональных данных как public
❌ Неправильно: profile с public.
✅ Правильно: private или no-store.
Почему: можно утечь чужие данные.
✅ Правильно: private или no-store.
Почему: можно утечь чужие данные.
Ошибка 3: Нет 304
❌ Неправильно: всегда 200.
✅ Правильно: 304 при отсутствии изменений.
Почему: экономия трафика и времени.
✅ Правильно: 304 при отсутствии изменений.
Почему: экономия трафика и времени.
Ошибка 4: Нет Vary
❌ Неправильно: один кэш для всех языков.
✅ Правильно: Vary: Accept-Language.
Почему: иначе пользователи получат чужой язык.
✅ Правильно: Vary: Accept-Language.
Почему: иначе пользователи получат чужой язык.
Ошибка 5: Не сбрасывают кэш после изменений
❌ Неправильно: старые данные после PUT.
✅ Правильно: сбрасывать кэш.
Почему: данные устаревают.
✅ Правильно: сбрасывать кэш.
Почему: данные устаревают.
Ошибка 6: no-store забывают для чувствительных данных
❌ Неправильно: кешировать токены.
✅ Правильно: no-store для секретов.
Почему: безопасность.
✅ Правильно: no-store для секретов.
Почему: безопасность.
Best Practices
- Всегда задавай Cache-Control.
- Используй ETag или Last-Modified.
- Возвращай 304 при отсутствии изменений.
- Персональные данные — private или no-store.
- Сбрасывай кэш после изменений.
- Используй Vary для языков и форматов.
Заключение
Кэширование делает API быстрым и стабильным.
Но оно требует правил и дисциплины.
Если кэш настроен правильно — выигрывают все.
✅ Вывод: хороший кэш = быстрый API и довольные пользователи.