Missed the action at this year's Chrome Dev Summit? Catch up with our playlist on YouTube. Watch now.

HTTP-кеширование

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

Во всех браузерах есть встроенный HTTP-кеш. Нам осталось убедиться, что в каждом ответе сервер указывает правильные директивы HTTP-заголовков. Они нужны, чтобы указать браузеру, когда и на какой период нужно кешировать ответ.

HTTP-запрос

Когда сервер возвращает запрос, он также отправляет набор HTTP-заголовков, описывающих тип контента, длину, команды для работы с кешем, маркер подтверждения и т. д. Например, в примере выше сервер возвращает запрос размером 1024 Б, отдает команду клиенту кешировать его на 120 секунд и отправляет маркер подтверждения (x234dff). Он используется, чтобы проверить, не изменился ли ресурс, после того как срок действия ответа истек.

Подтверждение кешированных ответов с помощью ETags

TL;DR

  • Сервер отправляет маркер подтверждения в HTTP-заголовке ETag.
  • С помощью маркера подтверждения можно проверить, изменился ли ресурс.

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

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

Пример HTTP Cache-Control

В примере выше клиент автоматически отправляет маркер ETag в HTTP-заголовке If-None-Match. Сервер проверяет совпадение маркера с нужным ресурсом, и если тот не изменился, отправляет ответ 304 Not Modified. Это значит, что кешированный ответ остался прежним, и его можно снова использовать в течение следующих 120 секунд. Обратите внимание, что скачивать ресурс ещё раз не нужно. Это уменьшает время загрузки страницы и экономит пропускную способность канала.

Чем полезна проверка актуальности для разработчика? Браузер сделает все автоматически: проверит, был ли указан маркер подтверждения ранее, добавит его в исходящий запрос и обновит временные отметки на основе ответа от сервера. Все, что нам нужно сделать, - проверить, действительно ли сервер отправляет нужные маркеры ETag. Чтобы узнать, какие параметры следует установить, ознакомьтесь с документацией к серверу.

Cache-Control

TL;DR

  • Правила кеширования ресурса можно указать с помощью HTTP-заголовка Cache-Control.
  • Директивы Cache-Control определяют, где, как и на какое время может быть кеширован ресурс.

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

Пример HTTP Cache-Control

no-cache и no-store

Директива no-cache означает, что при повторном запросе к тому же URL ответ можно использовать только после проверки изменений. Таким образом, если указан соответствующий маркер подтверждения (ETag), будет выполнена повторная проверка. Однако при отсутствии изменений данные не будут скачиваться ещё раз.

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

public и private

Если в ответе содержится директива public, его можно кешировать, даже если с ним связана HTTP-аутентификация и код статуса ответа обычно нельзя сохранять. Эта директива требуется редко, потому что другая информация, заданная для кеширования, (например, max-age) показывает, что ответ можно сохранить.

Директива private, наоборот, используется для ответов, которые можно сохранить в кеше браузера, но не в промежуточных кешах. Это происходит из-за того, что подобная информация предназначается для одного пользователя. Например, HTML-страницу с личными данными пользователя можно кешировать в браузере, но не в сетях доставки контента.

max-age

Эта директива указывает период времени в секундах, в течение которого скачанный ответ может быть повторно использован. Отсчет начинается с момента запроса. Например, max-age=60 означает, что ответ может быть кеширован и использован в течение 60 секунд.

Выбор правил Cache-Control

Схема выбора кеширования

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

Директивы Cache-Control Значение
max-age=86400 Ответ можно сохранить в браузере и промежуточных кешах (для него указана директива"public") на 1 день (60 секунд x 60 минут x 24 часа)
private, max-age=600 Браузер может кешировать ответ на 10 минут (60 секунд x 10 минут)
no-store Ответ нельзя кешировать. При повторном запросе нужно скачать его заново.

По данным HTTP Archive, на 300 000 самых популярных сайтов по рейтингу Alexa около половины скачанных запросов можно сохранить в кеше браузера. Это помогло бы сэкономить огромные объемы данных при повторном посещении страниц. Однако это не означает, что в вашем приложении обязательно есть 50% ресурсов, которые можно сжать. Некоторые сайты сохраняются в кеше более чем на 90%, а на других размещено много личной или меняющейся со временем информации, которую кешировать невозможно.

Проверьте ваши страницы, определите, какие ресурсы можно кешировать, и убедитесь, что они возвращают правильные заголовки Cache-Control и ETag.

Аннулирование и обновление кешированных ответов

TL;DR

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

Все HTTP-запросы сначала направляются браузером в его кеш, чтобы проверить наличие действительного сохраненного ответа. Если совпадение найдено, ответ считывается из кеша, уменьшая время загрузки сайта и объем скачиваемых данных. Однако нам может потребоваться обновить кешированный ответ или сделать его недействительным. Что нам предпринять в этом случае?

Предположим, мы задали команду кешировать таблицу стилей CSS в течение 24 часов (max-age=86400), но чуть позже обновили дизайн сайта и хотим, чтобы это увидели все пользователи. Как сообщить им, что нужно обновить устаревшую копию CSS в кеше? Это непросто, потому что нам потребуется изменить URL ресурса.

Кешированный ответ сохраняется в браузере до тех пор, пока не истечет срок его действия или пока он не будет удален, например при очистке кеша браузера. Таким образом, во время изменения страницы не все пользователи работают с одинаковыми версиями файла: те, кто загрузил ресурс недавно, видят новую версию, а те, кто кешировал предыдущую (но ещё действительную) копию, - старую.

Как совместить кеширование ресурсов и обновления сайта? Можно просто отредактировать URL файла, чтобы пользователь скачивал новый ответ каждый раз, когда его контент меняется. Для этого добавьте в имя файла идентификационную метку или номер версии, например style.x234dff.css.

Иерархия кешей

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

  • Для HTML указана директива no-cache, поэтому браузер будет проверять актуальность документа при каждом запросе и при изменениях скачивать последнюю версию ресурса. Кроме того, мы изменили HTML-разметку, добавив идентификационные метки в URL CSS- и JavaScript-ресурсов. Если эти файлы изменятся, HTML тоже обновится, и браузер скачает новую копию HTML-ответа.
  • Браузерам и промежуточным кешам (например, сетям доставки контента) разрешено сохранять CSS. Срок его действия заканчивается через 1 год. Обратите внимание, что мы можем установить такой длинный период, потому что мы добавили идентификационную метку в название файла. Поэтому если CSS обновится, то URL тоже изменится.
  • Срок действия для JavaScript тоже истекает через 1 год. Однако ресурс отмечен директивой private, потому что в нем содержатся личные данные пользователи, которые нельзя сохранять в кеше сетей доставки контента.
  • У кешированного изображения нет версии или уникальной идентификационной метки, и срок его действия заканчивается через 1 день.

Таким образом, с помощью ETag, Cache-Control и уникальных URL мы можем достичь нужных нам результатов: долгих сроков действия, контроля над кешированием ресурса и обновлений в любой момент.

Список методов оптимизации

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

Помните о некоторых советах и техниках, которые помогут вам выбрать стратегию кеширования:

  1. Используйте одинаковые URL для одного ресурса. В противном случае контент каждый раз будет скачиваться заново. Помните, что в URL регистр букв имеет значение!
  2. Убедитесь, что сервер отправляет маркер подтверждения (ETag). Если ресурс на сервере не изменился, то благодаря этому маркеру те же байты не будут передаваться повторно.
  3. Определите, какие ресурсы можно сохранить в промежуточных кешах. Чаще всего это ответы, которые одинаковы для всех пользователей.
  4. Определите подходящий срок действия для каждого ресурса. У данных могут быть разные требования к частоте обновления информации. Учитывая это, выберите подходящее значение max-age для каждого ресурса.
  5. Установите подходящую иерархию кешей для вашего сайта. Используйте URL ресурсов с идентификационными отметками контента и короткие сроки действия (или директиву no-cache) для HTML-документов. С их помощью вы можете указать, когда кешированные версии данных будут обновлены.
  6. Уменьшите пересылку данных. Если часть ресурса, например функции JavaScript или наборы CSS-стилей, обновляется часто, отправляйте ее код в отдельном файле. Тогда та часть контента, которая меняется редко, например коды библиотек, может быть загружена из кеша. Это уменьшит количество скачиваемых данных при обновлении ресурса.