В отличие от модели кэширования, принятой в НТТР/1.0, НТТР/1.1 предлагает более изощренный механизм управления кэшем c помощью заголовка Cache-Control. Он обеспечивает управление процессом кэширования и со стороны отправителя, и со стороны получателя при помощи набора директив. Спецификацией протокола определено всего 12 директив. В основном директивы предназначаются только прокси-серверам и лишь некоторые из них воспринимаются и браузерами.

Протокол HTTP/1.0 предполагал расширение директив для заголовка Pragma, как совокупность всех директив для прокси-серверов. Но в HTTP/1.1 отказались от этого и ввели отдельный заголовок Cache-Control для управления кешированием, в котором содержатся в том числе и директивы для прокси-северов. Другие директивы для прокси-серверов, сгрупированы в других новых заголовках. А для заголовка Pragma новых директив вводится не будет (см. заметку про заголовок Pragma).

Управление кэшированием в НТТР/1.1 по большей части является симметричным. И серверы, и клиенты имеют одинаковые возможности управлять кэшированием ответов. Сервер может установить режим кэширования конкретного ответа, и эти установки будут иметь приоритет над любыми установками, используемыми кэшами по умолчанию. Поэтому список директив и запроса и ответа достаточно широк. Несмотря на то, что некоторые директивы ответа и запроса имеют одинаковое название, их действие порой различно, а так же может различаться формат представления.

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

Директивы запроса
no-store		для прокси: кэшам не разрешено сохранять сообщение
no-transform		для прокси: нельзя изменять тип данных ресурса
no-cache		для прокси: отправлять запрос дальше исходному серверу
only-if-cached		для прокси: требование ресурса только из кэша
max-age		для прокси: возраст ответа должен быть не больше указанного значения
max-stale		для прокси: возможен устаревший ответ, но не старше указанного значения
min-fresh		для прокси: ответ должен быть актуальным по меньшей мере указанное время
  • no-store Директива no-store не допускает coхранения сообщения в кэше. Стандарт RFC 2616 запрещает (уровень MUST NOT) прокси-серверам кэшировать запрос, а также любой ответ на него, если в запросе присутствует эта директива.
    Зачем клиенту может потребоваться включать в запрос эту директиву? Очевидно только с единственной целью - по соображениям безопасности и конфиденциальности. Но как раз эта единственная цель и является весьма спорной. Данная директива не дает никаких гарантий конфиденциальности и полагаться на нее никак нельзя. Если клиент действительно заинтересован скрыть данные запроса и ответа, он должен обмениваться данными по защищенному(шифрованному) протоколу.
    Может быть поэтому браузеры не реализовывают эту функцию. По крайней мере я не нашел в браузерах ни единой возможности отправлять запросы с директивой no-store.
  • no-transform Директива no-transform не допускает (уровень MUST NOT), чтобы кэш каким-либо образом изменял содержимое сообщения. Прокси-сервера иногда преобразуют содержимое сообщения, перед тем как положить его в свой кэш. Преобразование может заключаться в:
    1. Изменение типа (например, изображение может быть переведено в другой формат)
    2. Сжатие
    3. Любое другое изменение содержимого (например, уменьшение изображения или снижение его качества)
    Любые изменения делаются прокси-серверами из соображений экономии дискового пространства для хранения кэша и снижения затрат на передачу сообщений.
    Подобные изменения могут быть крайне нежелательными. Если речь идет о необратимом изменении, то происходит утрата части информации, которая может оказаться важной для клиента. Но даже если речь идет об обратимом изменении (сжатии), то оно будет недопустимо в случае наличия конрольной суммы. При любом изменении содержимого контрольная сумма не сойдется и сообщение будет отвергнуто.
  • no-cache Директива запроса no-cache абсолютно идентична аналогичной директиве заголовка Pragma. Любому прокси-серверу на пути между отправителем и исходным-сервером не разрешено (MUST NOT) возвращать ответ из кэша без предварительной успешной проверки актуальности у исходного сервера. Это гарантирует пользователю, что он получит ответ с исходного сервера. Этот процесс известен как "сквозная перезагрузка" (end-to-end reload). Когда пользователь нажимает кнопку F5(Обновить) при зажатой клавише Ctrl, браузер посылает директиву Cache-Control: no-cache, а некоторые и Pragma: no-cache.
    Несмотря на то, что при каждом запросе нужно перепроверять актуальность ресурса, кэширование этого ресурса не лишено смысла. Это становится понятным, если вспомнить о методе проверки актуальности при помощи заголовка ответа Last-Modified и заголовка запроса If-Modified-Since (см. статью Управление кэшированием со стороны сервера). Директива no-cache может быть отправлена вместе с заголовком запроса If-Modified-Since. При чем это может сделать промежуточный прокси-сервер, если он имеет у себя кэшированную копию ресурса, даже если браузер такой заголовок не посылал. В этом случае исходный сервер может ответить 304 Not Modified. В этом случае будет возвращена кэшированная копия ресурса.
    Обратите внимание, что старый заголовок Pragma: no-cache мог присутствовать только в запросе. Директива no-cache нового заголовка Cache-Control может присутствовать и в заголовках ответа (см. ниже).
  • only-if-cached Противоположностью директивы запроса no-cache является директива only-if-cached. В этом случае, отправителю нужен ответ только из кэша. Может быть, отправитель озабочен задержкой ответа или его не беспокоит актуальность кэшированного ответа. Кэш может послать ответ, если это соответствует условиям запроса, но в других обстоятельствах может вернуть код ответа 504 Gateway Timeout. Прокси-сервер может вернуть заведомо устаревший ответ, но только если запрос не содержит других ограничений, таких как заголовка If-Modified-Since.
  • max-age Эта директива запроса предписывает, что ответ должен иметь "возраст" меньше, чем значение времени (в секундах), определенное в директиве запроса max-age. Таким образом, директива max-age=0 форсирует сквозную проверку актуальности (end-to-end revalidation).
  • max-stale Директива запроса max-stale выражает готовность клиента принимать ответы из кэша, срок годности которых возможно истек. Однако время истечения срока годности ответа не должно превышать значение, указанное в директиве (если оно имеется). Если значение директивы не задано, клиент готов принять любой ответ. Директива max-stale позволяет клиентам обойти правила, которыми руководствуется прокси-сервер. Директива max-stale=60 означает, что пользователь готов принять ответ, который может быть устарел, но не более чем на минуту.
  • min-fresh Директива min-fresh выражает желание клиента принять ответ, который будет актуальным по меньшей мере указанное число секунд. Если пользователь делает запрос с директивой min-fresh=60, то ответ должен быть актуальным, как минимум 60 секунд.

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

Директивы ответа
no-store		для клиентов и прокси: кэшам не разрешено сохранять сообщение
no-transform	для прокси: нельзя изменять тип данных ресурса
no-cache		для клиентов и прокси: не обслуживать из кэша без проверки актуальности
public		для клиентов и прокси: разрешение кэшировать ответ, где угодно
private		для прокси: не кэшировать в совместно используемых кэшах
must-revalidate	для клиентов и прокси: возвращать только актуальные ответы
proxy-revalidate	для прокси: возвращать только актуальные ответы
max-age		для клиентов и прокси: срок актуальности ответа
s-maxage		для прокси: срок актуальности ответа в совместно используемых кэшах
  • no-store Директива ответа no-store аналогична такой же директиве запроса и используется, чтобы не допустить сохранения ответа в кэше. Сервер может принять решение, что переданные им данные не подлежат сохранению по различным соображениям. Эта директива относится и к прокси-серверам и к браузерам.
  • no-transform Директива ответа no-transform аналогична такой же директиве запроса и используется, чтобы не допустить изменения содержимого ответа.
  • no-cache В отличие от аналогичной директивы запроса, данная директива имеет два формата представления: со значением и без. Эта директива представляет собой команду отсроченного действия.
    Включая в ответ директиву no-cache без значения сервер разрешает кэшам сохранять его, но с одним условием: в случае запроса клиентом данного ресурса, кэш должен будет предварительно проверить актуальность этого ресурса у исходного сервера.
    Может показаться, как и в случае с аналогичной директивой запроса, что прокси-серверам и браузерам совершенно не выгодно кэшировать такие сообщения. Так может показаться, если забыть о методе проверки актуальности при помощи заголовка ответа Last-Modified и заголовка запроса If-Modified-Since (см. статью Управление кэшированием со стороны сервера). Директива no-cache заставит клиента каждый раз запрашивать ресурс с исходного сервера. Если клиент отправлял запрос с заголовком If-Modified-Since (а это возможно только в случае, если ресурс уже хранится в кэше), то сервер может ответить 304 Not Modified. Тогда браузер или прокси-сервер вернет ресурс из своего кэша.
    Существует так же формат директивы со значением. В качестве значения должно выступать имя одного или нескольких заголовков данного ответа (например, Cache-Control: no-cache=location или Cache-Control: no-cache="location, set-cookie"). В случае передачи директивы со значением, действие этой директивы распространяется только на указанные заголовки. Таким образом, когда клиент запросит данный ресурс, кэш в обязательном порядке должен проверить актуальность только указанных заголовков у исходного-сервера. Вероятно делать он это будет с помощью метода HEAD, что значительно ускоряет процедуру проверки. Остальную часть сообщения он вправе вернуть из кэша.
  • public Принимая во внимание, что кэш, возможно, решит не сохранять определенные ответы из опасения нарушить их конфиденциальность, сервер, включая заголовок ответа Cache-Control: public, может показать, что данный ответ можно сохранять в кэше.
  • private Директива private препятствует сохранению ответа в совместно используемом кэше. Таким образом сохраненный в кэше ответ может быть использован только одним пользователем, которому он предназначался до этого. Речь идет в основном о кэше браузера. Кэши прокси-серверов по определению являются совместно используемыми, поэтому данная директива не позволит сохранять сообщение на прокси-серверах.
    Область действия директивы private может быть сужена со всего сообщения до всего лишь одного или нескольких заголовков ответа путем добавления значения директивы. Например, отправив заголовок Cache-Control: private="set-cookie" сервер тем самым разрешит сохранять ответ где угодно, однако совместно используемые кэши перед сохранением должны будут исключить из сообщения заголовок Set-Cookie.
  • must-revalidate Эта директива вынуждает кэш провести дополнительную проверку актуальности ресурса на сервере-источнике, если он не уверен в его актуальности. Если прокси-сервер не может осуществить проверку актуальности на сервере-источнике (например, если прокси-сервер не может связаться с Web-сервером), то кэш должен вернуть клиенту не устаревший ответ, а сообщение об ошибке. Различие между директивой must-revalidate и похожей на первый взгляд директивой no-cache заключается в том, что директива must-revalidate накладывает менее строгие ограничения. Директива no-cache строго предписывает при каждом запросе ресурса проверять его актуальность, не зависимо от того, устарел ответ в кэше или нет. Директива must-revalidate позволяет прокси-серверу не выполнять проверку актуальности, если он уверен, что его копия сообщения актуальна. Об этом он может судить по другим директивам ответа и заголовкам ответа. Подобное поведение оставляет небольшую вероятность того, что возвращенный ответ может отличаться от ответа исходного-сервера.
    Директива must-revalidate допускает сохранение ответа, по гарантирует, что устаревшие ответы будут обновляться. Однако ответ, который кэш считает обновленным, может быть уже изменен на сервере-источнике. Это различие между директивами no-cache и must-revalidate является ключевым для понимания необходимости обеих директив. Директива no-store вообще исключает кэширование. В случае no-cache существует возможность кэширования, но отсутствует риск возвращения ответа, который уже изменен. В случае must-revalidate существует реальная возможность кэширования и не существует возможности устаревания, хотя остается возможность возвращения ответа, который уже был изменен.
    Необходимость этой директивы становится очевидной в свете существования директивы запроса max-stale, которая допускает возможность возвращения устаревшего ответа. Директива must-revalidate является предписанием со стороны сервера-источника игнорировать директивы запроса max-stale и возвращать всегда только актуальный ответ.
  • proxy-revalidate Директива ответа proxy-revalidate является менее ограничительной, чем директива must-revalidate. В то время как последняя относится ко всем кэшам, proxy-revalidate относится только к совместно используемым, т.е. кешам прокси-серверов, о чем можно судить по названию директивы. Кэш браузера игнорирует данную директиву.
  • max-age Директива ответа max-age является предпочтительным способом задать срок годности содержимого. Директива должна передаваться с числовым значением в секундах, например Cache-Control: max-age=60. Наличие директивы ответа max-age отменяет любое значение заголовка Expires при вычислении периода актуальности кэшированного ответа. Заголовок Expires относится к протоколу HTTP/1.0. Хотя он и оставлен в новой версии протокола HTTP/1.1 по соображениям совместимости, но наряду с этим при помощи директивы ответа max-age введен новый механизм задания времени актуальности сообщения, который перекрывает старый метод.
  • s-maxage Данная директива во всем аналогична директиве ответа max-age за тем лишь исключением, что она адресована только совместно используемым кэшам. Если она присутствует в ответе, то для прокси-сервера она будет предпочтительнее директивы max-age, и уж тем более предпочтительнее старого заголовка Expires. Кэши браузеров игнорируют директиву s-maxage.

Протокол HTTP/1.1 так же допускает вписывать в заголовок Cache-Control новые директивы, расширяющие возможности стандартного протокола. Это так называемые лексемы расширений. Если кешу та или иная директива неизвестна, он должен попросту ее игнорировать.

Комментарии:

  1. Den

     

    Спасибо. Поделюсь онлайн инструментом для проверки заголовков Cache-control:
    http://highloadtools.com/cachecontrol

  2. Евгений

     

    Я хочу использовать тег Cache-control: max-age=31536000. Нужно ли обязательно этот тег сопровождать одним из тегов валидации If-modified-since/E-tag, или по-умолчанию браузер будет кешировать ответ на указанный период?

    • Автор

       

      Не нужно. Заголовок Cache-control является самодостаточным, ему не нужны какие-то дополнительные заголовки для того, чтобы он начал действовать.
      P.S. Заголовок If-Modified-Since - это заголовок запроса, а заголовок ETag - это заголовок ответа. Они никак не могут стоять рядом. Я так понял вы говорите про ответ.

    • Евгений

       

      Спасибо. Я имел ввиду Last-modified вместо If-modified-since.

    • Автор

       

      Евгений, заголовки Last-modified и If-modified-since на мой взгляд не относятся к вопросам кеширования. Эти заголовки реализуют механизм проверки актуальности, который я описал в статье "Управление кэшированием со стороны сервера". Это разные механизмы (кэширование и проверка актуальности), которые могут использоваться вместе или раздельно.
      Пусть вас не вводит в заблуждение тот факт, что браузер кэширует ответ с заголовком Last-modified. Без явного ответа сервера 304 Not Modified браузер не может отдать этот кэшированный ответ. На мой взгляд это никакое не кэширование, если при КАЖДОМ запросе надо спрашивать у сервера, можно вернуть ответ из кэша или нет.
      Совсем другое дело с заголовком Cache-control: max-age=31536000. Пока не истечет срок, браузер даже спрашивать у сервера ничего не будет. Это настоящее кэширование.