предисловие

Cache-Control

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

решение задач

Управление кэшированием со стороны сервера

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

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

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

Запрет кэширования

Я не буду сейчас рассматривать те решения, которые предлагаются другими авторами. Многие из них или не работают, или избыточны. Некоторые распространенные ошибки обсуждаются в заметке к заголовку Pragma.
Если следовать стандарту RFC 2616, то потребовать от кэшей возвращать ответ только от исходного сервера при каждом обращении можно тремя способами.

HTTP
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: max-age=0, must-revalidate

Проверка кэширования

Проверку кэширования в браузерах надо проводить правильно, иначе неверный способ может ввести Вас в заблуждение. Некоторые статьи на тему кэширования предлагают проверять кэширование страницы с помощью возврата к ней через кнопки браузера "Назад" и "Вперед". Это неправильно. Это корректно работает только в браузере Internet Explorer, и то, я не уверен, что во всех версиях. А такие браузеры как Opera и FireFox в таких случаях ВСЕГДА берут страницу из кэша, но это не значит что они игнорируют действие управляющих директив.
Еще более неправильно пользоваться клавишей F5 для обновления страницы. В этом случае, напротив, все браузеры будут перезагружать страницу, запрашивая ее с исходного сервера.
Правильный способ проверки кэширования - загрузить тестируемую страницу в новом окне браузера или новой вкладке. В этом случае вы увидите разницу между методом с помощью кнопок "Назад" и "Вперед" - браузеры Opera и FireFox будут следовать предписаниям управляющих директив, за некоторыми небольшими исключениями.

Чем по сути отличаются эти способы вы можете узнать прочитав описание заголовка Cache-Control, но результат у них будет один. В третьем способе можно обойтись и без директивы must-revalidate, большинство браузеров все равно будут обновлять кэш. Но для верности и в соответствии со стандартом не лишним будет эту директиву указать. Но эти способы не всегда работают. Например браузер Opera 8 игнорирует директиву no-store и кэширует ресурс.

Но я призываю вас не использовать все три способа одновременно. Это лишь покажет вашу безграмотность. Выберете только один, который наиболее подходит в вашей ситуации.
Я рекомендую использовать третий способ, но с указанием не нулевого срока актуальности. Ведь очень редко бывает, что ресурс меняется каждую секунду. А не редко происходит сутуация, когда пользователь в течение короткого времени возвращается к вашему ресурсу. За это время он наверняка не изменится. Определитесь с этим сроком и укажите его в директиве max-age (период актуальности указывается в секундах).

Кэширование на время

Кэшировать ресурс на определенный период можно было уже в HTTP/1.0 при помощи заголовка Expires. В HTTP/1.1 этот метод оставлен по соображениям совместимости, но предложен новый метод с директивой max-age заголовка Cache-Control, который при наличии перекрывает действие заголовка Expires. Итак, рассмотрим три варианта кэширования на время.
HTTP
Expires: Sun, 21 Feb 2010 17:28:32 +0300
Cache-Control: max-age=60, must-revalidate
Cache-Control: max-age=60, s-maxage=30, must-revalidate

В заголовке Expires должна быть указана дата будущего, когда срок актуальности ресурса истечет. Этот метод можно было бы привести в пример еще в предыдущей главе, касаемо запрета кэширования, указав в заголовке текущее время. Но, во-первых, тот факт, что актуальность ресурса истекла еще не гарантирует, что кэш его обновит. В новом протоколе есть принудительная директива must-revalidate, в старом протоколе ничего подобного нет. А, во-вторых, сегодня этот заголовок поддерживает только браузер FireFox.
Второй метод нам уже знаком из предыдущего параграфа. Его поддерживают все браузеры. Третий метод лишь расширяет его, указывая, что в совместно используемых кэшах ресурс следует обновлять чаще. При этом нет никакого смысла указывать в директивах max-age и s-maxage одинаковые значения.

Метод проверки актуальности

Этот механизм появился еще в протоколе HTTP/1.0 и успешно используется и в новом протоколе. Этот метод работает при помощи заголовков Last-Modified и If-Modified-Since. Механизм проверки актуальности реализуется в отношении ресурса с истекшим сроком актуальности. Однако его можно использовать и в отрыве от метода кэширования на время. Тогда актуальность будет проверяться при каждом запросе ресурса. Алгоритм этого метода следующий:

1. При первом требовании ресурса сервер отправляет заголовок Last-Modified, где указывает время последнего изменения.

HTTP
HTTP/1.0 200 OK
Date: Sun, 21 Feb 2010 17:28:32 +0300
Last-Modified: Sat, 20 Feb 2010 15:10:14 +0300

2. Браузер кэширует этот ресурс и при следующем требовании ресурса отправляет запрос с заголовком If-Modified-Since, в который включает дату из заголовка Last-Modified, который получил в предыдущий раз.

HTTP
GET /index.php HTTP/1.0
Date: Sun, 21 Feb 2010 21:32:52 +0300
If-Modified-Since: Sat, 20 Feb 2010 15:10:14 +0300

3. Сервер получив заголовок If-Modified-Since сверяет дату последнего изменения ресурса с датой из этого заголовка. Если дата изменилась, то происходит стандартный ответ с кодом состояния 200 OK и заголовком Last-Modified, где указывается новая дата последнего изменения (возвращаемся к пункту 1).
Если же ресурс не изменился, то сервер отвечает строкой состояния с кодом 304 Not Modified и, самое главное, не возвращает содержимого ресурса.

HTTP
HTTP/1.0 304 Not Modified
Date: Sun, 21 Feb 2010 21:33:15 +0300

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

Но это еще не все...

Эта заметка не претендует на полноту изложения вопроса. Здесь я не коснулся специфических заголовков, которые так же касаются методов проверки актуальности и кэширования. Для более полного ознакомления с вопросом кэширования в HTTP/1.1 я рекомендую прочитать описания заголовков Age и Vary, а по вопросам проверки актуальности прочитать описания заголовков Etag, If-Match, If-None-Match, Range, If-Range, If-Unmodified-Since.

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

  1. 10 сентября 2014, 21:58

    Андрей

    комментарий No. 6

    День добрый. Несколько раз видел, что динамический контент отдается с "Cache-control: no-cache, must-revalidate". Соответственно, возник вопрос: "При таком хедере какое значение будет приоритетнее, или вместе эти значения обаладают каким-то особым смыслом?".

    • 11 сентября 2014, 08:51

      Автор

      ответ No. 1

      Нет, вместе они никакой магической силой не обладают. Нельзя говорить о приоритетности. Почитайте в статье о заголовке Cache-Control на этом сайте (в предисловии к этой статье), что написано про директиву must-revalidate. Там четко описано, чем отличаются no-cache и must-revalidate.
      no-cache налагает более строгие условия, поэтому в данном случае, выражаясь вашей терминологией, он будет "приоритетнее", а must-revalidate в данном примере избыточен.

    Ответить на комментарий..

Ваш комментарий