Найти тему

Cache-Control + ETag. Кэширование запросов

Оглавление

Для кэширование запросов при наличии версии или другого признака актуальности удобно использовать связку заголовков Cache-Control и ETag (If-None-Match). Подойдет любой признак актуальности, который меняется при обновлении информации.

Допустим нам надо закэшировать информацию по справочным данным. У нас есть GET метод запроса справочника, в котором мы указываем имя справочника в качестве параметра. При этом мы знаем версию этого справочника на сервере (или можем легко получить из бд). Важно, что мы хотим контролировать когда нужно обновить кэшированные данные (момент когда данные и версия обновятся). И хотим сэкономить на передачи по сети повторяющихся данных.

Что нужно для работы кэша?

  1. Вычислить и установить заголовок ответа ETag, это может быть версией или другим признаком актуальности, к примеру так: W/"dict_A_version_2.3.1"
  2. Установить заголовок ответа Cache-Control, например так: private, max-age=1209600, no-cache
  3. В обработчике сервера сравнить вычисленный ранее ETag с полученным заголовком запроса If-None-Match (Его добавит браузер если у него будет кэш нужной части).
  4. Вернуть данные со статусом 200 или пустой ответ со статусом 304 Not Modified.
  5. Не забыть снять галочку Disable cache во вкладке Network :)

Схема работы кэша:

Упрощенная схема работы браузерного кэша
Упрощенная схема работы браузерного кэша
  • Клиент запрашивает у сервера информацию по справочнику A.
  • Сервер анализирует справочник, видит версию 2.3.1 и формирует будущий заголовок ETag = W/"dict_A_version_2.3.1".
  • Сервер возвращает клиенту справочную информацию (допустим список размером 2МБ) с проставлением двух заголовков ответа Cache-Control и ETag.
  • Браузер запоминает полученный ответ для этого запроса и значение заголовка ETag.
  • Клиент запрашивает у сервера информацию по справочнику A второй раз, при это браузер проставляет заголовок If-None-Match с ранее полученным значением заголовка ETag (Браузер не проставит этот заголовок если кэша нет, он просрочен или очищен).
  • Сервер анализирует справочник, видит версию 2.3.1 и сново формирует будущий заголовок ETag = W/"dict_A_version_2.3.1".
  • Сервер сравнивает вычисленный ETag и полученное значение в заголовке If-None-Match.
  • Если они равны, сервер ДОЛЖЕН отправить пустой ответ со статусом 304 Not Modified, говоря тем самым браузеру взять информацию из кэша (при этом по сети отправится не много байт, а не исходный ответ).
  • Если они не равны, сервер возвращает новые данные с новым заголовком ETag.

Когда справочник поменяется, то поменяется и его версия. Когда сервер будет сравнить полученное значение в заголовке If-None-Match и новосформированный ETag, то увидит не соответствие и отправит новые данные, которые у себя обновит браузер.

Можно также поиграться с настройками Cache-Control, с такими как время жизни, способами проверки и прочими. Подробнее можно почитать тут: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
Про слабые и сильные валидаторы ETag и что туда обычно пишут можно почитать тут:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
В особых случаях можно использовать Last-Modified, почитать тут:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified