25 Ноябрь 2015

Кеширование средствами браузера

Содержание

  1. Вводная часть.
  2. Как посмотреть http заголовки в Chrome.
  3. Анализ http-заголовков, отвечающих за кеширование.
  4. Http-заголовки, отвечающие за клиентское кеширование.
  5. Какие http заголовки должны быть для правильной настройки кеширования.
  6. Какие сущности кешировать.
  7. Как настроить пользовательское кеширование в WordPress.
  8. Проблема кеширования html-файлов в WordPress
  9. Получить плагин браузерного кеширования html-страниц для WordPress
  10. Файлы изобрежний, листы стилей (style.css), скрипты (scripts.js)
  11. Что делать, если сущность изменилась, а срок годности кеширования не истек.

Если посмотреть на рекомендации Гугла по скорости загрузки страницы, можно увидеть требования к использованию кеша браузера. Иногда требование кеширования находится в красной области, иногда в желтой.

Google Page Speed

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

Яндекс: Следите за корректностью http-заголовков. В частности, важно, содержание ответа, который сервер отдает на запрос «if-modified-since». Заголовок Last-Modified должен отдавать корректную дату последнего изменения документа.

Как видно из цитаты хелпа, Яндекс требует корректную дату в заголовке Last-Modified. А заголовок Last-Modified имеет прямое отношение к кеширированию. Еще одно очко в пользу необходимости разобраться с кешированием.

Яндекс грозит проблемами: Даже если сервер не выдает дату последней модификации документа (last-modified), ваш сайт будет проиндексирован. Однако в этом случае следует учитывать следующее:

  • в результатах поиска не будет показываться дата рядом со страницами вашего сайта;
  • при сортировке по дате сайт не будет виден большинству пользователей;
  • робот не сможет получить информацию о том, обновилась ли страница сайта с момента последнего индексирования. А так как число страниц, получаемых роботом с сайта за один заход, ограничено, изменившиеся страницы будут переиндексироваться реже.

Гугл таких ужасов в хелпе не пишет. Просто информирует о возможности снизить нагрузку и ускорить загрузку за счет кеширования (это мы уже и так знаем).

Гугл: Убедитесь, что ваш веб-сервер поддерживает HTTP-заголовок "If-Modified-Since". Этот заголовок позволит веб-серверу сообщать Google, изменился ли контент сайта со времени последнего сканирования. Поддержка этой функции сокращает издержки и нагрузку на полосу пропускания.

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

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

Хочу заметить, что есть два типа кеширования: кеширование на стороне клиента и на стороне сервера. В данной статье речь идет о первом типе кеширования. Однако, с целью снижения нагрузок на сервер (если вы хотите остаться на более дешевом тарифе хостера при росте посещаемости), необходимо применять и серверное кеширование страниц. Я писал о своем опыте в снижении нагрузок благодаря установке MaxCache на WordPress.

Вводная часть

Итак, что мы знаем о кешировании на стороне клиента, т.е. в браузере посетителя сайта. Оказывается, существует некие http-заголовки, отдаваемые сервером браузеру, которые мы визуально на сайте не наблюдаем, но они сообщают браузеру определенную служебную информацию. Часть передаваемых заголовков отвечает за кеширование. Например, на указанный в заголовках период, браузер сохранит загруженную страницу и до истечения указанного срока годности будет отображать локально сохраненную копию.

Пару слов о том, какая еще информация содержится в http-заголовках. В заголовках часто передается информация о кодировке страницы, которая оказывается приоритетной над информацией, указанной в теге meta раздела head. Еще в заголовках может содержаться информация о Pingback-ах, которую спамеры используют для своих черных дел, с этим можно бороться. Также в заголовках передается код ответа сервера (404, 200, 301…).

В плане настроек кеширования из всей массы http-заголовков, нас будут интересовать четыре заголовка из группы ответа сервера или Response Headers: Cache-Control, Expires, ETag, Last-Modifed и два заголовка из группы запроса браузера или Request Headers: If-Modified-Since, If-None-Match.

Как посмотреть http заголовки в Chrome

Сначала нужно понять тот факт, что заголовки для каждой сущности на странице будут свои. Под сущностью я понимаю следующее:

  • сам html-код страницы, открытой в браузере;
  • картинки, используемые на странице;
  • другие подключаемые в коде страницы дополнительные файлы (style.css, scripts.js, библиотека jquery…).

Как я писал выше, без ухищрений http-заголовки не увидеть. Для их просмотра на анализируемой странице в Google Chrome нужно открыть инспектор кода и перейти на вкладку Network, после чего обновить страницу. В инспекторе кода будет наблюдаться процесс загрузки сущностей. Клик по каждой из сущностей позволит просмотреть http-заголовки выбранной сущности.

Инспектор кода Google Chrome, вкладка Network

В самом верху списка сущностей будет html-код загруженной в браузере страницы. Ниже можно наблюдать файлы изображений, используемые в контенте данной страницы, листы стилей, файлы скриптов и т.д.

Анализ http-заголовков, отвечающих за кеширование

Рассмотрим http-заголовки загруженной в браузере html-страницы. Для этого нужно кликнуть по названию соответствующей сущности в инспекторе кода. Ориентируясь на скриншот выше — нужно кликнуть по выделенному красной рамкой названию «rabochee-prostranstvo-9». Результат (на скриншоте ниже) отобразит все http-заголовки для данной сущности (сущность в данном случае — это html-страница, открытая в браузере и только она без дополнительных файлов, используемых в ее контенте).

Анализ http-заголовков сущности

Вы без труда отыщите группы заголовков Response Headers и Request Headers.

Наша задача проанализировать заголовки отвечающие за кеширование html-страницы на стороне пользователя. Также обратим внимание на заголовок Status Code раздела General.

 

Http-заголовки, отвечающие за клиентское кеширование

Разберем каждый из http-заголовков, имеющих отношение к клиентскому кешированию.

Status Code

Касательно Status Code скажу сделующее.

  • Код 200 OK — сущность штатно благополучно отдается сервером и грузится пользователю в полном объеме.
  • Код 304 Not Modified — с последнего посещения пользователем страницы сущность, сохраненная в кэш браузера, не изменилась; а значит нет смысла занимать канал, тратить трафик и грузить ее снова, можно показать пользователю сохраненную в кэше (на компьютере пользователя) копию.
  • Информацию о значении других кодов можно посмотреть здесь.

Теперь подробно знакомимся с заголовками, отвечающими за кеширование в группе Response Headers и Request Headers. К Status Code будем возвращаться в процессе.

На анализируемой вами сущности необязательно будут присутствовать все заголовки, описанные ниже. И это нормально.

Expires

Данный заголовок в качестве значения содержит дату, до которой кэш считается действительным.

Действия браузера разворачиваются в следующем духе.

  1. Пользователь первый раз перешел на страницу. Пока нет никакого кэширования и браузеру в заголовке Status Code отдается значение 200 OK для сущности «html-страница» (если со страницей все впорядке, разумеется). Html-файл загружается с сервера.
  2. Если при этом в заголовках содержится Expires, то сущность будет сохранена на компьютере пользователя в кеше до даты, переданной в значении этого заголовка.
  3. При следующем обращении пользователя к странице браузер не будет запрашивать сущность с сервера до истечения срока годности кеша (указанного в значении заголовка Expires). Сущность будет грузится из локальной копии.

Аналогичный алгоритм действий происходит со всеми сущностями, используемыми на анализируемой странице.

Cache-Control

Данный заголовок в качестве значений может содержать несколько инструкций:

  • max-age — количество секунд в течении которых кеш считается актуальным;
  • no-cache — наличие инструкции сообщает, что кешировать можно, но при каждом обращении нужно проверять изменения сущности, хранящейся на сервере, относительно кеша (о том, как осуществляется эта проверка, пойдет речь ниже);
  • no-store — вообще запрещает сохранять сущность в кеш;
  • private или public — первая инструкция запрещает, а вторая разрешает кешировать страницу на промежуточных устройствах (роутерах, например);
  • остальные директивы в этой публикации рассматривать не вижу смысла.

Если сравнить задачи заголовка Expires и Cache-Control: max-age, то они окажутся идентичными.

Expires и Cache-Control: max-age нужны для того, чтобы указать срок годности кеша. На указанный период времени браузер будет грузить сущность из кеша не обращаясь к серверу и не проверяя изменения.

Гугл в одной инструкции советует использовать только Expires и именно его. Одновременно указывать оба заголовка Гугл считает чрезмерным; при этом прямо на странице инструкции присутствуют оба заголовка (хотя это ни о чем не говорит). В другой инструкции говорится, что современным и полностью поддерживаемым окажется Cache-Control: max-age.

На большинстве анализируемых мной сайтов я обнаружил оба заголовка Expires и Cache-Control: max-age. К счастью, содержащаяся в них информация не противоречила друг-другу.

ETag и If-None-Match

В значении заголовка ETag передается набор символов, идентифицирующий состояние сущности. При любых изменениях в сущности меняется значение заголовка.

В качестве значения заголовка может быть передана либо просто версия сущности (цифра 1, 2, 3…) либо хэш (набор символов, составленный на основе контента сущности и уникальный для каждого его состояния).

Поведение браузера при получении метки Etag

  • Получив заголовок ETag браузер должен сохранить его значение и, когда время кеширования, указанное заголовками Expires и/или Cache-Control: max-age истечет, браузер отправит запрос If-None-Match, в значении которого будет содержаться ранее сохраненная метка ETag.
  • Сервер сравнит актуальное значение ETag сущности и присланное в заголовке If-None-Match.
  • Если метки одинаковы — изменения сущности не происходило и ее можно грузить из кеша. При этом актуальность кеша снова продлится на период, указанный в Expires и/или Cache-Control: max-age.
  • Одинаковы метки или нет проверяет сервер. Если метки одинаковы — сервер отправляет заголовок Status Code со значением 304 Not Modified и браузер грузит страницу из кеша. Если метки разные или это первое посещение сущности — мы получим код 200 OK, сущность в полном объеме будет грузится с сервера.

Last-Modifed и If-Modified-Since

В значении заголовка Last-Modifed хранится дата последней модификации сущности.

Алгоритм действий отличается от вышеописанного варианта с ETag только тем, что в случае получения браузером заголовка Last-Modifed, он сохранит дату, указанную в Last-Modifed и будет проверять актуальность кеша отправляя серверу заголовок If-Modified-Since с сохраненной датой.

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

По сути дела ETag и Last-Modifed выполняют одинаковые функции: информируют об изменении сущности с момента последнего обращения.

Гугл советует использовать один из заголовков (в этот раз, любой) и не указывать оба одновременно.

В начале статьи я писал о желании Яндекса видеть в http-заголовках Last-Modifed. В результате выбор сделали за нас :)

Загадка для внимательного читателя. Так что использовать-то? ETag или Last-Modifed??? Есть те, кому не понятно?

Какие http заголовки должны быть для правильной настройки кеширования

Подитожим. Основываясь на рекомендациях Гугла и Яндекса, а также всего вышесказанного, делаем вывод.

  1. Заголовок Expires или Cache-Control: max-age должен быть у той сущности, которую мы собираемся кешировать.
  2. Заголовок Last-Modifed также должен быть у кешируемой сущности.
  3. Современные реалии говорят о том, что Expires все время идет в паре с Cache-Control: max-age. Главное, чтобы они друг-другу не противоречили.

Какие сущности кешировать

Какие сущности нам подвластны:

  • html-код страницы;
  • картинки;
  • наши файлы скриптов;
  • файл стилей.

Именно заголовкам html-файла нужно уделить особое внимание, т.к. их никто не настраивает, а мнение Яндекса по этому поводу вы уже знаете.

По поводу неподвластного нам не паримся:

  • виджеты комментариев, например, тот же disqus, используемый на этом блоге;
  • загружаемый с серверов Гугла jQuery;
  • ну и т.д.

В одной книге по jQuery рекомендовали эту самую библиотеку jQuery грузить с серверов Гугла, а не как по-умолчанию в WordPress сделано: из недр самого движка. Если сейчас посмотреть на заголовки, неподвластные нам, посылаемые сервером Гугла при загрузке jQuery (прямо на этой странице смотрите, этот сайт хоть и на WP, но я настроил загрузку библиотеки откуда надо), то мы увидим, что кэшироуется она на год. И доля вероятности, что пользователь посетил перед моим замечательным ресурсом другой сайт, где тоже используется jQuery с сервера Гугла с кешированием на год, очень высока. Соответственно, посетителю не прийдется снова грузить jQuery на моем сайте, библиотека есть в его локальном кеше. Та-дааа!

Как настроить пользовательское кеширование в WordPress

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

Главная страница

Речь идет о кешировании браузером именно html-файла главной страницы.

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

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

Возможно, будет также правильным все же указать для главной страницы Cache-Control: max-age со значением в несколько часов. Потому как дергать каждый раз сервер проверкой актуальности кеша не очень рационально. Это повод для дискуссии в комментариях, милости прошу. Весь вопрос в том, на сколько сильно напрягается сервер при проверке актуальности кеша и как часто обновляется контент вашей главной страницы.

Совершенно для всех html-страниц должен быть указан заголовок Last-Modified.

Для главной страницы датой для Last-Modified может служить, например, дата добавления свежего поста, который попал на главную. Т.е. любое изменение контента главной должно привести к установке свежего значения Last-Modified.

Если главная страница статична, то смотрите мои соображения о кешировании страницах публикации.

Страница рубрики

Мои соображения по кешированию рубрики полностью совпадают с соображения по динамической главной странице.

Страница отдельной публикации.

Вот здесь можно использовать Cache-Control: max-age. Думаю, суток кеширования будет достаточно. Допустим, контент публикации вы обновляете не часто, но состояние ссылок в сайдбаре на свежие или популярные посты будет заморожено на срок годности кеша.

Но не стоит забывать про комментарии. Если вы используете сторонний сервис комментариев (disqus или cackle), то он сам отвечает за кеширование выводимых комментариев и можно не париться. А если комментарии стандартные, от движка, то нужно учитывать частоту обновления комментариев.

Проблема кеширования html-файлов в WordPress

По умолчанию WordPress не кеширует html-страницы и не передает заголовок Last-Modified. А соображения, изложенные выше, говорят о необходимости присутствия Last-Modified на всех страницах для сущности «html-страница».

Вот как выглядят http-заголовки страницы отдельной публикации в WP по-умолчанию:

Htpp-заголовки страницы публикации

Last-Modified нет, Etag нет. Страница не попадет в кеш.

На самом деле, из-за таких заголовков, html-файл постоянно грузится с сервера, кеширование для него не работает. В подтверждение этому мы видим код 200 OK при каждом обновлении страницы.

Очевидно, для решение вопроса нужно применять плагин. Я проанализировал разнообразные плагины браузерного кеширования для WordPress. Но ни один не показал адекватной работы. Быстро написать плагин для решения задачи у меня не получилось, т.к. есть сложность одновременной работы с плагинами серверного кеширования. Нужно все внимательно продумать и найти решение. Я оставлю форму подписки ниже. После того, как плагин будет готов, я отправлю его на указанный адрес эл. почты.

На указанный e-mail я отправлю плагин браузерного кеширования.


Файлы изобрежний, листы стилей (style.css), скрипты (scripts.js)

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

На сколько часто вы меняете изображения в постах, оставляя при этом имя файла картинки неизменным? Думаю, картинки постов и изображения оформления страницы — это весьма стабильные сущности. Им можно давать длительный срок для нахождения в кеше браузера без проверок. Максимальный срок — год, возможно это то, что нужно.

Аналогичная ситуация с листами стилей и файлами скриптов. Меняются они не часто. Пару недель кеширования без права проверки?

Что делать, если сущность изменилась, а срок годности кеширования не истек

Предположим, вы внесли изменения в лист стилей. Это бывает часто. И, предположим, вы хоститесь на Бегете. Это бывает еще чаще.

Бегет так настроил кеширование, что обновления листа стилей не будут проверяться неделю (Cache-Control:max-age=604800). Вы увидите изменения только если нажмете на кнопку «обновить» в браузере, обычный переход на страницу вызовет загрузку листа стилей из локального кеша. Получается, посетитель увидит ваши обновления через неделю.

Если задуматься, то проблема может быть не такой страшной. Я не думаю, то найдется много посетителей, которые ходят на ваш сайт каждый день. Но все же. Чтобы заставить постоянных посетителей обновить кеш листа стилей до истечения его срока действия достаточно просто изменить имя файла. Новый файл = новый кеш.

Был просто style.css, а станет style.css?v=1. Каждое обновление листа стилей и мы меняем цифру. В результате пользователь видит всегда свежее оформление.

Автор статьи: Андрей Морковин.