December 10, 2021

Кэширование в HTTP: механизмы клиентского и серверного кэша в HTTP

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

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

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

Так же хочу обратить ваше внимание на то, что механизмы кэширования HTTP сервера (например, веб-сервер Apache) и HTTP клиента (любой браузер) зависят непосредственно от разработчика, сейчас мы рассматриваем общие принципы кэширования протокола HTTP без привязки к какому-либо приложению.

Правильность кэширования в HTTP

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

  1. Проверка достоверности. Сервер, который дает ответ на запрос должен всегда быть в курсе о содержимом конечного сервера, к которому идет обращение.
  2. Новизна кэша. Новизна кэша определяется конечным сервером.
  3. Предупреждение. Транзитный сервер должен предупреждать клиента о том, что его информация из кэша не самая «свежая».

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

Предупреждения при кэшировании в HTTP

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

Механизмы управления кэшированием в HTTP. Поле заголовка Cache-Control в HTTP

В версии протокола HTTP 1.1 в качестве механизмов управления кэшированием используется поле заголовка Cache-Control. Заголовок Cache-Control позволяет управлять HTTP кэшем, как клиенту, так и HTTP серверу при помощи директив, которые они передают вместе с ответами и запросами. При этом директивы, передаваемые в поле заголовка Cache-Control, отменяют значения по умолчанию для кэширования в HTTP. Необходимо отметить тот момент, что директивам заголовка Cache-Control должны повиноваться все участники цепочки обмена данными между конечным сервером и клиентом.

Cache-control: no-cache

1 Cache-control: no-cache

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

Номер Директивы поля заголовка Cache— Control для клиента и их описание 1 Директива поля заголовка Cache— Control для клиентского запроса: no— cache

Директива no-cache говорит серверу о том, что для последующего запроса ответ не должен отправляться из кэша без проверки с содержимым исходного сервера. 2 Директива поля заголовка Cache— Control для клиентского запроса: no— store

Директива no-store говорит серверу о том, что ни запрос клиента, ни ответ сервера не должны кэшироваться. Это сделано для безопасности. 3 Директива поля заголовка Cache— Control для клиентского запроса: max— age = seconds

Директива max-age говорит серверу о том, что кэш должен быть не старше времени, которое указано в секундах.
4 Директива поля заголовка Cache— Control для клиентского запроса: max— stale [ = seconds ]

Директива max-stale говорит серверу о том, что клиент примет кэшированный HTTP ответ в том случае, если его возраст не превышает времени, указанного в секундах.
5 Директива поля заголовка Cache— Control для клиентского запроса: min— fresh = seconds

Директива min-fresh говорит серверу о том, что клиент примет кэшированный HTTP ответ в том случае, если время жизни кэша не больше указанных секунд.
6 Директива поля заголовка Cache— Control для клиентского запроса: no— transform

Директива min-fresh говорит серверу о том, что к запрашиваемому ресурсу не должно применяться никаких преобразований.
7 Директива поля заголовка Cache— Control для клиентского запроса: only— if— cached Директива min-fresh говорит серверу о том, что клиент примет только кэшированный HTTP ответ, если подходящего ответа нет в кэше сервера, то делать ничего не надо.

Мы рассмотрели механизм управления кэшированием HTTP клиентом, теперь давайте разберемся с механизмами управления кэшированием со стороны HTTP сервера. Другими словами: как HTTP ответы могут управлять кэшированием. Управление кэширование со стороны сервера происходит аналогичным образом – при помощи заголовка Cache-Control, но директивы заголовка Cache-Control у сервера свои и, соответственно, свои механизмы управления кэшированием при HTTP соединение. Давайте сведем серверные директивы заголовка Cache-Control в одну таблицу.

Номер Директивы поля заголовка Cache— Control для сервера и их описание
1 Директива поля заголовка Cache— Control ответа сервера: public

Директива ответа сервера Public говорит о том, что ответ сервера можно сохранять в любом кэше.
2 Директива поля заголовка Cache— Control ответа сервера: private

Директива ответа сервера private говорит о том, что ответ сервера нужно сохранять в закрытом кэше, который предназначен только для этого пользователя.
3 Директива поля заголовка Cache— Control ответа сервера: no— cache

Директива ответа сервера no— cache говорит о том, что кэшированный ответ не должен отправляться клиенту без его предварительной проверки.
4 Директива поля заголовка Cache— Control ответа сервера: no— store

Директива ответа сервера no— store говорит о том, что ответ сервера нельзя сохранять в кэше.
5 Директива поля заголовка Cache— Control ответа сервера: no— transform

Директива ответа сервера no— transform говорит о том, что к ответу сервера не должно применяться никаких преобразований ни одним из узлов цепочки.
6 Директива поля заголовка Cache— Control ответа сервера: must— revalidate

Директива ответа сервера must— revalidate говорит о том, что если HTTP сообщение сервера устарело, то к нему должна применяться предварительная проверка.
7 Директива поля заголовка Cache— Control ответа сервера: proxy— revalidate

Директива ответа сервера proxy— revalidate говорит о том, что и предыдущая директива, но только для промежуточных серверов.
8 Директива поля заголовка Cache— Control ответа сервера: max— age = seconds

Директива ответа сервера max— age говорит о том, сколько живет кэш на сервере.
9 Директива поля заголовка Cache— Control ответа сервера: s— maxage = seconds

Директива ответа сервера s— maxage говорит о том, что и директива max-age, но для CDN-серверов

Общий синтаксис механизмов кэширования в HTTP вы сможете найти ниже.

Мы рассмотрели механизмы управления кэшированием в HTTP, давайте теперь посмотрим, как приложения определяют, что кэш устарел и какими правилами руководствуются.

Модель устаревания кэша в HTTP

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

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

Модель сравнения кэша в HTTP

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

Например, клиент делает повторный запрос к HTTP серверу к тому же самому URI, при этом контент по данному URI никак не изменился, сервер проанализировал всю эту информацию и вместо того, чтобы отправлять ответ с телом сообщения (HTTP объектом), в котором может быть огромный HTML документ с таблицами стилей и скриптами, сервер просто дает ответ с кодом состояния 304 (Not Modified), после чего браузер подтягивает страничку из кэша, а конечный пользователь думает, что ему эта страница загрузилась с сервера из Австралии.

Помимо условных запросов для модели сравнения HTTP кэша используется поле заголовка Last-Modified, этот заголовок используется сервером и в него записывается дата последнего изменения документа (дата и время в HTTP) и если дата последнего известного изменения совпадает с той датой, что указана в заголовке Last-Modified, то контент, запрашиваемый пользователем, отдается из кэша, если эта дата не совпадает, то ответ дается новый, при этом происходит кэширование и перезапись поля заголовка Last-Modified.

Помимо всего прочего, модель сравнения кэша в HTTP может использовать для проверки актуальности кэша тэг HTTP объекта, который записывается в поле заголовка ETag и который уникален для каждого объекта. Например, в нашем HTML документе был текст Hello, world!!!, а мы его изменили на Hello, World…, так вот значение поля ETag для таких страниц будет разным. Такой подход сравнения кэша в HTTP считается более надежным, так как позволяет избегать временных парадоксов, но в то же время, такой подход более сложный и затратный.