Основы клиентского кэширования понятными словами и на примерах. Last-modified, Etag, Expires, Cache-control: max-age и другие заголовки

белка

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

В рамках этой статьи мы попытаемся разобраться с клиентским кэшированием. В частности, разберемся с тем, какие http-заголовки используются браузерами и веб-серверами и что они значат.

Но для начала давайте выясним, зачем вообще нужно кэширование на стороне клиента?.

Веб-страницы состоят из множества различных элементов: картинок, css и js файлов и т.п. Часть этих элементов используются на нескольких (многих) страницах сайта. Под клиентским кэшированием понимают способность браузеров сохранять копии файлов (ответов сервера), чтобы не загружать их повторно. Это позволяет значительно ускорить повторную загрузку страниц, сэкономить на трафике, а также снизить нагрузку на сервер.

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


Http заголовки для управления клиентским кэшированием

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

Без кэша (при отсутствии кэширующих http-заголовков)

no-cache

Как мы видим, каждый раз при отображении картинки cat.png браузер будет снова загружать ее с сервера. Думаю, не нужно объяснять, что это медленно и неэффективно.


Заголовок ответа Last-modified и заголовок запроса if-Modified-Since.

Идея заключается в том, что сервер добавляет заголовок Last-modified к файлу (ответу), который он отдает браузеру.

Last-modified: Fri, 1 Dec 2014 01:01:01 GMT

Теперь браузер знает, что файл был создан (или изменен) 1 декабря 2014. В следующий раз, когда браузеру понадобится тот же файл, он отправит запрос с заголовком if-Modified-Since.

if-Modified-Since: Fri, 1 Dec 2014 01:01:01 GMT

Если файл не изменялся, сервер отправляет браузеру пустой ответ со статусом 304 (Not Modified). В этом случае, браузер знает, что файл не обновлялся и может отобразить копию, которую он сохранил в прошлый раз.

last-modified

Таким образом, используя Last-modified мы экономим на загрузке большого файла, отделываясь пустым быстрым ответом от сервера.


Заголовок ответа Etag и заголовок запроса If-None-Match.

Принцип работы Etag очень схож с Last-modified, но, в отличии от него, не привязан ко времени. Время — вещь относительная.

Идея заключается в том, что при создании и каждом изменении сервер помечает файл особой меткой, называемой ETag, а также добавляет заголовок к файлу (ответу), который он отдает браузеру:

ETag: "686897696a7c876b7e"

Теперь браузер знает, что файл актуальной версии имеет ETag равный «686897696a7c876b7e». В следующий раз, когда брузеру понадобится тот же файл, он отправит запрос с заголовком If-None-Match: "686897696a7c876b7e".

If-None-Match: "686897696a7c876b7e"

Сервер может сравнить метки и, в случае, если файл не изменялся, отправить браузеру пустой ответ со статусом 304 (Not Modified). Как и в случае с Last-modified браузер выяснит, что файл не обновлялся и сможет отобразить копию из кэша.

etag

Подробнее о ETag можно почитать здесь.


Заголовок Expired

Принцип работы этого заголовка отличается от вышеописанных Etag и Last-modified. При помощи Expired определяется «срок годности» («срок акуальности») файла. Т.е. при первой загрузке сервер дает браузеру знать, что он не планирует изменять файл до наступления даты, указанной в Expired:

Expired: Fri, 1 Mar 2014 01:01:01 GMT

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

expires

Такой вид кэша особенно актуален для иллюстраций к статьям, иконкам, фавиконкам, некоторых css и js файлов и тп.


Заголовок Cache-control с директивой max-age.

Принцип работы Cache-control: max-age очень схож с Expired. Здесь тоже определяется «срок годности» файла, но он задается в секундах и не привязан к конкретному времени, что намного удобнее в большинстве случаев.

max-age

Для справки:

  • 1 день = 86400 секунд
  • 1 неделя = 604800 секунд
  • 1 месяц = 2629000 секунд
  • 1 год = 31536000 секунд

К примеру:

Cache-Control: max-age=2629000;

У заголовка Cache-control, кроме max-age, есть и другие директивы. Давайте коротко рассмотрим наиболее популярные:

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

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

no-cache
Позволяет указать, что клиент должен делать запрос на сервер каждый раз. Иногда используется с заголовком Etag, описанным выше.

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

must-revalidate
Эта директива предписывает браузеру делать обязательный запрос на сервер для ре-валидации контента (например, если вы используете eTag). Дело в том, что http в определенной конфигурации позволяет кэшу хранить контент, который уже устарел. must-revalidate обязывает браузер при любых условиях делать проверку свежести контента путем запроса к серверу.

proxy-revalidate
Это то же, что и must-revalidate, но касается только кэширующих прокси серверов.

s-maxage
Практически не отличается от мах-age, за исключением того, что эта директива учитывается только кэшем резличных прокси, но не самим браузером пользователя. Буква «s-» исходит из слова «shared» (например, CDN). Эта директива предназначена специально для CDN-ов и других посреднических кэшей. Ее указание отменяет значения директивы max-age и заголовка Expired. Впрочем, если вы не строите CDN-сети, то s-maxage вам вряд ли когда-либо понадобится.


Как посмотреть, какие заголовки используются на сайте?

Вы можете посмотреть заголовки http-запросов (request headers) и ответов (response headers) в отладчике Вашего любимого браузера. Вот например, как это выглядит в хроме:

cache-chrome

То-же самое можно увидеть в любом уважающем себя браузере или http-сниффере.


Настройка кэшировения в Аpache и Nginx

Мы не будем пересказывать документацию по настройке популярных серверов. Вы всегда можете посмотреть ее здесь для Nginx и здесь для Apache. Ниже мы приведем несколько примеров из жизни для того, чтобы показать, как выглядят файлы конфигурации.

Пример конфигурации Apache для контроля Expires

Выставляем различный «срок годности» для различных типов файлов. Один год для изображений, один месяц для скриптов, стилей, pdf и иконок. Для всего остального — 2 дня.

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType text/x-javascript "access plus 1 month"
  ExpiresByType image/x-icon "access plus 1 year"
  ExpiresDefault "access plus 2 days"
</IfModule>

Пример конфигурации Nginx для контроля Expires

Выставляем различный «срок годности» для различных типов файлов. Одна неделя — для изображений, один день — для стилей и скриптов.

server {

    #...


    location ~* \.(gif|ico|jpe?g|png)(\?[0-9]+)?$ {
        expires     1w;
    }

       location ~* \.(css|js)$ {
        expires     1d;
    }

    #...

}

Пример конфигурации Apache для Cache-control (max-age и public/private/no-cache)

<ifModule mod_headers.c>
  <FilesMatch "\.(gif|ico)$">
      Header set Cache-Control "max-age=2592000, public"
  </FilesMatch>

  <FilesMatch "\.(js)$">
      Header set Cache-Control "max-age=88000, 
              private, must-revalidate"
  </FilesMatch>

  <FilesMatch "\.(php)$">
      Header set Cache-Control "private, no-store, no-cache, 
              must-revalidate, no-transform, max-age=0"
      Header set Pragma "no-cache"
  </FilesMatch>
</ifModule>

Пример конфигурации Nginx для Cache-control статических файлов

server {

    #...


    location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
      add_header Cache-Control "max-age=88000,  public";
    }

    #...

}

В заключение

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

Будем благодарны за рекомендации и поправки в комментариях и не забудьте поделиться статьей с друзьями, если она Вам понравилась ;)

Отмечено как: , , ,
28 comments on “Основы клиентского кэширования понятными словами и на примерах. Last-modified, Etag, Expires, Cache-control: max-age и другие заголовки
  1. drcreazy:

    Отличная заметка Дмитрий!
    Из того, что можно добавить, Expires работает для HTTP/1.0 agents, а Cache-control: max-age= появилось в HTTP/1.1

    А так же, что для Etag мы как правило должны выполнить все теже действия что и при обычном запросе, чтобы посчитать hash, т.е экономия идет трафика но не CPU

  2. Роман:

    1 день в секундах = 88000

    Срочно смените свой калькулятор!

  3. Вадим Макишвили:

    Спасибо, Дима. Всё толково рассказал. Теперь понятно!

  4. Дмитрий Беглов:

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

  5. sait4seo:

    А что лучше использовать Expires или Cache-control, имеет ли смысл одновременно прописывать и то и другое? Для html-страничек у меня Last-modified, так как это ещё и для поисковых роботов правильно

  6. Сергей П.:

    Теперь ждём статью про Application Cache @ HTML5.

  7. Marina:

    Очень хорошо написана статья, удобно разбираться, и тем более очень понятно благодаря прекрасным картинкам))
    Вот только заголовок Expires, а не Expired.

  8. Китана:

    Спасибо, очень понятно написано! Очень доступно!

  9. Единственная статья в рунете, где раскрыта вся правда о Кэше и Клиенте!
    У меня домашний сервер уже 5 лет, нахожусь в Хабаровске. Кэш на стороне клиента пока не отладил, пока мучаюсь.
    Кто хочет попробовать скорость загрузки с личного сервера, заходи на ЕдаМоре.рф.
    Дмитрию респект и уважение!

    Petr de Cril’on

  10. Спасибо за подробную статью. Хотя, прочитав ее до конца, я так и не усвоил — где мне применить и какую конфигурацию именно. То ли я должен внедрить код в htacsess, то ли я должен хостера попросить об применении кэширования на сервере, то ли я сам могу сделать подобные настройки… Не понял. Прочту еще раз, может где что упустил… Мне бы на практике применить вышесказанное..

  11. Наиболее понятной статьи по кэшированию я не встречал у нас в рунете. Автору низкий поклон и огромное спасибо!

  12. Святослав:

    Спасибо. Очень просто и доступно изложено. Особенно порадовали картинки.

    Вопрос.

    Раньше у меня сайт был на Апаче, и я просто прописывал всё, что нужно в .htaccess. А теперь переехал на Nginх и никак не разберусь, как настроить кеширование. Дело в том, что хостинг виртуальный, доступа к серверу, естественно, нет.

    Есть ли возможность настроить кеширование средствами сайта? (php, WP)

  13. Андрей:

    Здравствуйте! Я прописал это в конфиге nginx:

    location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    add_header Cache-Control "max-age=88000, public";
    }

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

  14. Mo:

    Скажите, могу ли я использовать Cache-Control и одновременно с ним ETag, для того чтобы в случае изменения файлы были перекэшированы, даже если не достигнут max-age?…
    если нет, то как можно добиться перекэширования при изменениях?

  15. Китана:

    Классная статья, очень понятно все рассказано, спасибо!

  16. Alexander:

    Спасибо за хорошую статью!

    По-моему, есть опечатка с годами при описании заголовка Expired — то 2014-й упоминается, то 2015-й.

  17. Vasler:

    Как это использовать? Непонятно. Вставляю в .htacces

    ExpiresActive On
    ExpiresByType image/jpg «access plus 1 year»
    ExpiresByType image/jpeg «access plus 1 year»
    ExpiresByType image/gif «access plus 1 year»
    ExpiresByType image/png «access plus 1 year»
    ExpiresByType text/css «access plus 1 month»
    ExpiresByType application/pdf «access plus 1 month»
    ExpiresByType text/x-javascript «access plus 1 month»
    ExpiresByType image/x-icon «access plus 1 year»
    ExpiresDefault «access plus 2 days»

    совершенно ничего не дает

  18. emsi:

    объяснено получше чем в гугле https://developers.google.com/speed/docs/insights/LeverageBrowserCaching

    но по прежнему не понятно

    куда вставлять эти куски кода (указаны в примере)?
    есть ли DEMO на GitHub?

  19. Serg:

    А что означает last в таком случае? Подскажите, пожалуйста. По этой ссылке не совсем понятно:
    https://preply.com/question/chto-oznachaet-last

  20. I see your page needs some fresh & unique articles. Writing manually is time consuming, but there is solution for this.
    Just search for; Masquro’s strategies

  21. ЯЯЯЯЯ:

    Как быть с косяком при авторзиации? Ласт модиф потом лезет боком. Надо отключать кеш для старниц поиска и авторизации правильно я понял? Делать

    header(«Cache-Control: no-store, no-cache, must-revalidate, max-age=0»);
    header(«Cache-Control: post-check=0, pre-check=0», false);
    header(«Pragma: no-cache»);

  22. Alpi:

    Спасибо за статью, очень наглядно.

    Позанудствую, с вашего позволения. Может, кто-то прочитает и задумается.

    Про тягу к краткости конфигов неоднократно упоминал автор nginx Игорь Сысоев :)
    Регулярка выполняется на каждую картинку — не самое оптимальное распределение ресурсов.
    Если переделать в отдельные локейшены под каждый шаблон — серверу не придётся задумываться лишний раз при выдаче каждой конкретной картинки.

    location \.png { access_log off; log_not_found off; expires max; gzip off; }​
    location \.jpg { access_log off; log_not_found off; expires max; gzip off; }​

  23. Виктор:

    Опечатки: акуальности, кэшировения

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

*

* Copy This Password *

* Type Or Paste Password Here *

Проект создан в GanttPRO
Спасибо за лайк в FACEBOOK
Подписывайтесь на новости вконтакте
Последние статьи от html5.by