Содержание
Основы клиентского кэширования
Источник: нагло украл статью и ссылку не поставлю ..!..
Кэш играет важную роль в работе практически любого веб-приложения на уровне работы с базами данных, веб-серверами, а также на клиенте.
В рамках этой статьи мы попытаемся разобраться с клиентским кэшированием. В частности, разберемся с тем, какие http-заголовки используются браузерами и веб-серверами и что они значат.
Но для начала давайте выясним, зачем вообще нужно кэширование на стороне клиента?.
Веб-страницы состоят из множества различных элементов: картинок, css и js файлов и т.п. Часть этих элементов используются на нескольких (многих) страницах сайта. Под клиентским кэшированием понимают способность браузеров сохранять копии файлов (ответов сервера), чтобы не загружать их повторно. Это позволяет значительно ускорить повторную загрузку страниц, сэкономить на трафике, а также снизить нагрузку на сервер.
Существует несколько различных HTTP-заголовков для того, чтобы управлять процессами кэширования на стороне клииента. Давайте поговорим о каждом из них.
Http заголовки для управления клиентским кэшированием
Для начала давайте посмотрим, как сервер и браузер взаимодействуют при отсутствии какого-либо кэширования. Для наглядного понимания я попытался представить и визуализировать процесс общения между ними в виде текстового чата. Представьте на несколько минут, что сервер и браузер – это люди, которые переписываются друг с другом :)
Без кэша (при отсутствии кэширующих http-заголовков)
Как мы видим, каждый раз при отображении картинки 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
мы экономим на загрузке большого файла, отделываясь пустым быстрым ответом от сервера.
Заголовок ответа 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
можно почитать здесь.
Заголовок Expired
Принцип работы этого заголовка отличается от вышеописанных Etag
и Last-modified
. При помощи Expired
определяется "срок годности" ("срок акуальности") файла. Т.е. при первой загрузке сервер дает браузеру знать, что он не планирует изменять файл до наступления даты, указанной в Expired
:
Expired: Fri, 1 Mar 2014 01:01:01 GMT
В следующий раз браузер, зная, что "дата истечения срока годности" еще не наступила, даже не будет пытаться делать запрос к серверу и отобразит файл из кэша.
Такой вид кэша особенно актуален для иллюстраций к статьям, иконкам, фавиконкам, некоторых css и js файлов и тп.
Заголовок Cache-control с директивой max-age.
Принцип работы Cache-control: max-age
очень схож с Expired
. Здесь тоже определяется "срок годности" файла, но он задается в секундах и не привязан к конкретному времени, что намного удобнее в большинстве случаев.
Для справки:
- 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
Практически не отличается от mах-age
, за исключением того, что эта директива учитывается только кэшем резличных прокси, но не самим браузером пользователя. Буква "s-" исходит из слова "shared" (например, CDN). Эта директива предназначена специально для CDN-ов и других посреднических кэшей. Ее указание отменяет значения директивы max-age
и заголовка Expired
. Впрочем, если вы не строите CDN-сети, то s-maxage
вам вряд ли когда-либо понадобится.
Как посмотреть, какие заголовки используются на сайте?
Вы можете посмотреть заголовки http-запросов (request headers) и ответов (response headers) в отладчике Вашего любимого браузера. Вот например, как это выглядит в хроме:
То-же самое можно увидеть в любом уважающем себя браузере или 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"; } #... }
В заключение
"Кэшировать все то, что можно кэшировать" – хороший девиз для веб-разработчика. Иногда можно потратить всего несколько часов на конфигурацию и при этом значительно улучшить восприятие вашего сайта пользователем, значительно сократить нагрузку на сервер и сэкономить на трафике. Главное – не переусердствовать и настроить все правильно с учетом особенностей Вашего ресурса.
Обсуждение