Loading...

Производительность · 9 мин чтения

Cache-Control и max-age: практическое руководство

Cache-Control — это заголовок, который сообщает браузерам и CDN, как долго им можно переиспользовать ответ. Настроите правильно — повторные визиты будут мгновенными, а источник останется спокойным; ошибётесь — либо год будете отдавать устаревшие страницы, либо вовсе откажетесь от кэширования. Здесь каждая директива объяснена простым языком и даны готовые рецепты для трёх случаев, покрывающих почти любой сайт.

Updated

Cache-Control и max-age: практическое руководство

Кто слушает этот заголовок

Один ответ проходит через несколько кэшей: браузер посетителя (приватный — обслуживает одного человека), edge CDN (общий — обслуживает всех) и иногда прокси между ними. Cache-Control — это способ источника говорить со всеми ними разом, поэтому и существуют директивы, обращённые к каждому по отдельности.

Это разделение определяет всё остальное. Личный кабинет авторизованного пользователя может кэшироваться в его браузере и никогда не должен кэшироваться на edge, где его мог бы получить другой пользователь. С публичной маркетинговой страницей всё наоборот: кэшируйте её надолго на edge и ненадолго в браузере. Если сначала решить «приватный или общий», все остальные выборы становятся очевидными.

Директивы, которые действительно важны

max-age=N — ответ можно переиспользовать N секунд, ничего не переспрашивая. Главный регулятор.

s-maxage=N — то же самое, но только для общих кэшей (CDN). Когда он присутствует, edge подчиняется ему и игнорирует max-age, поэтому вы можете держать что-то на edge час, пока браузеры хранят это минуту.

public / private — public может хранить любой кэш; private ограничивает хранение отдельным браузером. Всё персональное должно быть private.

no-cache — храните, но перед каждым переиспользованием проверяйте актуальность у источника. Это дёшево, когда ничего не изменилось: сервер может ответить 304 Not Modified без тела.

no-store — никогда не записывайте это ни в какой кэш, ни в памяти, ни на диске. Приберегите это для действительно чувствительных ответов; это не «пожалуйста, посвежее», это «не храните копию вообще».

immutable — тело по этому URL никогда не изменится, поэтому не проверяйте актуальность даже при перезагрузке. Правдиво только для версионированных имён файлов.

stale-while-revalidate=N — после истечения срока можно ещё N секунд отдавать устаревшую копию, пока в фоне забирается свежая. Посетитель никогда не ждёт повторной загрузки.

Три рецепта, покрывающих большинство сайтов

Версионированная статика — имя файла меняется вместе с содержимым, поэтому URL безопасно кэшировать навсегда. Это самый крупный и самый безопасный выигрыш от кэширования из доступных.

HTML-страницы — URL остаётся прежним, а содержимое меняется, поэтому нужен короткий срок жизни. Небольшой max-age плюс stale-while-revalidate дают скорость без отдачи вчерашней страницы.

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

Готовые отправные точки — подстройте числа под ритм ваших релизов

# versioned assets: /js/app.a1b2c3.js
Cache-Control: public, max-age=31536000, immutable

# HTML pages (short at the browser, longer at the edge, no waiting on refresh)
Cache-Control: public, max-age=60, s-maxage=600, stale-while-revalidate=86400

# per-user pages: never at the edge
Cache-Control: private, no-store

# an API response that changes often but can lag a little
Cache-Control: public, max-age=0, s-maxage=30, stale-while-revalidate=60

no-cache против no-store: ошибка, которой стоит избежать

Они читаются как синонимы и ведут себя совершенно по-разному. no-cache разрешает хранение, но требует проверки актуальности перед переиспользованием — копия остаётся, и когда она ещё актуальна, сервер отвечает 304 без тела, что очень дёшево. no-store запрещает хранить ответ где бы то ни было.

Взять no-store, когда имелся в виду no-cache, — значит выбросить всю оптимизацию без всякой пользы: каждый запрос превращается в полную загрузку, даже если ничего не изменилось. Используйте no-store, только когда сама сохранённая копия и есть проблема: банковские выписки, страницы сброса пароля, всё, что не должно оставаться в кэше общей машины. Для «всегда показывать самое свежее» правильный и куда более дешёвый ответ — no-cache.

Как это взаимодействует с вашим CDN

Заголовки, которые отдаёт ваш источник, — это то, чему подчиняется edge, решая, как долго хранить копию, а значит, Cache-Control — панель управления всей вашей цепочкой доставки, а не деталь браузера. Отдайте s-maxage — и edge будет ему следовать; отдайте no-store — и edge вообще откажется кэшировать, так что каждый запрос пойдёт к вашему источнику, и вы фактически отключите CDN для этого ответа.

На cdn.com.tr вы можете также задать поведение кэширования для каждого правила доставки в панели, когда изменить заголовки приложения невозможно, — это удобно для старых приложений, которые лучше не трогать. А меняя то, что возвращает закэшированный URL, помните: edge всё ещё держит предыдущую копию, пока она не истечёт, — именно для этого и нужен пурдж.

Проверьте, что вы отдаёте на самом деле

Предположения о заголовках часто оказываются неверными — фреймворк, плагин или настройка веб-сервера по умолчанию нередко переопределяют то, что вы, как вам кажется, настроили. Проверьте одной командой на каждый тип URL и посмотрите и на версионированный файл статики, и на HTML-страницу: они должны выглядеть совершенно по-разному. Обратите внимание и на Set-Cookie в ответе, который вы собирались кэшировать публично: многие кэши отказываются хранить такие ответы, и это частая причина, по которой страница загадочным образом никогда не кэшируется.

Прочитайте реальные заголовки ответа

# see what the edge and origin actually say
curl -sI https://example.com/ | grep -i "cache-control\|age\|set-cookie"

# compare a versioned asset (should be a long max-age)
curl -sI https://example.com/js/app.a1b2c3.js | grep -i cache-control

Частые вопросы

Какой max-age хорош для HTML-страниц?

Короткий — от секунд до нескольких минут, потому что URL остаётся прежним, а содержимое меняется. Сочетайте его с более длинным s-maxage на edge и stale-while-revalidate, чтобы посетители получали мгновенный ответ, пока обновление происходит в фоне.

Нужен ли Expires рядом с Cache-Control?

Нет. Cache-Control имеет приоритет над Expires и побеждает везде, где присутствуют оба. Expires важен только для очень старых клиентов; отдавать его дополнительно безвредно, но пользы это не приносит.

Почему моя страница не кэшируется даже с длинным max-age?

Чаще всего из-за заголовка Set-Cookie в ответе, директивы private или no-store где-то в цепочке либо строки запроса, из-за которой каждый запрос становится уникальным ключом кэша. Прежде чем менять конфигурацию, посмотрите реальные заголовки ответа.

Immutable действительно означает «навсегда»?

Он означает «тело по этому URL не изменится», поэтому кэши полностью пропускают проверку актуальности. Это верно только для версионированных имён файлов. Поставить immutable на URL, который вы потом перезапишете, — верный способ оставить посетителей со старым файлом без внятного способа это исправить.