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


  • htaccess кэширование сохраняет содержимое веб-страницы на локальном компьютере, когда пользователь посещает ее;
  • Использование кэша браузера – веб-мастер дает указания браузерам, как следует рассматривать ресурсы.

Когда браузер отображает веб-страницу, он должен загрузить логотип, CSS файл и другие ресурсы:

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

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

Как включить кэширование в браузере
  • Измените заголовки запроса ресурсов, чтобы использовать кэширование;
  • Оптимизируйте свою стратегию кэширования.
Изменение заголовков запроса

Для большинства людей единственный способ кэширования сайта htaccess заключается в том, чтобы добавить код в файл .htaccess на веб-сервере.

Файл .htaccess контролирует многие важные настройки для вашего сайта.

Кэширование браузера через файл.htaccess

Приведенный ниже код указывает браузеру, что именно кэшировать и как долго это «запоминать «. Его следует добавить в начало файла .htaccess :

## EXPIRES CACHING ## ExpiresActive On ExpiresByType image/jpg "access 1 year" ExpiresByType image/jpeg "access 1 year" ExpiresByType image/gif "access 1 year" ExpiresByType image/png "access 1 year" ExpiresByType text/css "access 1 month" ExpiresByType text/html "access 1 month" ExpiresByType application/pdf "access 1 month" ExpiresByType text/x-javascript "access 1 month" ExpiresByType application/x-shockwave-flash "access 1 month" ExpiresByType image/x-icon "access 1 year" ExpiresDefault "access 1 month" ## EXPIRES CACHING ##

Сохраните файл .htaccess , а затем обновите веб-страницу.

Как установить время кэширования для различных типов файлов

В приведенном выше коде заданы промежутки времени. Например, 1 year (1 год ) или 1 month (1 месяц ). Они связаны с типами файлов. Приведенный выше код устанавливает, что .jpg файлы (изображения ) следует кэшировать в течение года.

Если бы вы хотели изменить это, чтобы и JPG изображения кэшировались в течение месяца, то вы бы просто заменили «1 год » на «1 месяц «. Указанные выше значения кэширования через htaccess оптимальны для большинства веб-страниц.

Метод альтернативного кэширования для.htaccess

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

Возможно, что метод Expires не сработает на вашем сервере, в этом случае вы возможно захотите попробовать использовать Cache-Control .

Cache-Control

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

Пример использования в файле .htaccess :

# 1 Month for most static assets Header set Cache-Control "max-age=2592000, public"

Приведенный выше код устанавливает заголовок Cache-Control в зависимости от типа файла.

Как работает Cache-Control

Рассмотрим упомянутую выше строку кода кэширования в браузере htaccess :

# 1 Month for most static assets

Данная строка — просто примечание. Файл .htaccess игнорирует строки, начинающиеся с символа # . Это примечание рекомендуется, так как у вас может быть несколько различных наборов данных в качестве решения для кэширования файлов:

Упомянутая выше строка говорит, что, «если файл будет одним из этих типов, то мы сделаем что-то с ним… »

Самое важное в этой строке то, что в ней перечислены различные типы файлов (CSS , JS , JPEG , PNG и т.д. ) и что инструкции кэширования следует применять к этим типам файлов. Например, если вы не хотите, чтобы JPG файлы кэшировались в течение указанного периода времени, можете удалить «JPG «. Если вы хотите добавить HTML , то нужно в этой строке указать «HTML «:

Header set Cache-Control "max-age=2592000, public"

В упомянутой выше строке установлены фактические заголовки и значения:

  • Часть «Header set Cache-Control » — устанавливает заголовок;
  • Переменная «max-age=2592000 » – указывает, сколько времени займет процесс кэширования (в секундах ). В этом случае мы осуществляем кэширование в течение одного месяца (2592000 ) секунд;
  • Часть «public » сообщает о том, что это общедоступно.

Эта строка кэширования через htaccess закрывает оператор и заканчивает блок кода.

Общая проблема кэширования

Если вы составляете список изображений, которые будут кэшироваться в течение года и более, помните, что если вы вносите изменения в свои страницы, они могут быть не видны всем пользователям. Так как пользователи обратятся к кэшируемым файлам, а не к существующим. Если есть файл, который вы периодически редактируете (например — файл CSS ),то можно преодолеть проблему кэша с помощью цифрового отпечатка URL .

Цифровой отпечаток URL

Получение нового (некэшируемого) файлового ресурса возможно при наличии уникального имени. Например, если файл CSS назван «main.css», то вместо этого мы могли бы назвать его «main_1.css». В следующий раз, когда мы поменяем его имя, мы можем назвать файл «main_2.css». Это полезно для файлов, которые периодически изменяются.

Подключая внешние CSS и Javascript, мы хотим снизить до минимума лишние HTTP-запросы.

Для этого.js и.css файлы отдаются с заголовками, обеспечивающими надежное кеширование.

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

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

Простое кеширование ETag

Самый простой способ кеширования статических ресурсов - использование ETag .

Достаточно включить соответствующую настройку сервера (для Apache включена по умолчанию) - и к каждому файлу в заголовках будет даваться ETag - хеш, который зависит от времени обновления, размера файла и (на inode-based файловых системах) inode.

Браузер кеширует такой файл и при последующих запросах указывет заголовок If-None-Match с ETag кешированного документа. Получив такой заголовок, сервер может ответить кодом 304 - и тогда документ будет взят из кеша.

Выглядит это так:

Первый запрос к серверу (кеш чистый) GET /misc/pack.js HTTP/1.1 Host: сайт

Вообще, браузер обычно добавляет еще пачку заголовоков типа User-Agent, Accept и т.п. Для краткости они порезаны.

Ответ сервера Сервер посылает в ответ документ c кодом 200 и ETag: HTTP/1.x 200 OK Content-Encoding: gzip Content-Type: text/javascript; charset=utf-8 Etag: "3272221997" Accept-Ranges: bytes Content-Length: 23321 Date: Fri, 02 May 2008 17:22:46 GMT Server: lighttpd Следующий запрос браузера При следующем запросе браузер добавляет If-None-Match: (кешированный ETag): GET /misc/pack.js HTTP/1.1 Host: сайт If-None-Match: "453700005" Ответ сервера Сервер смотрит - ага, документ не изменился. Значит можно выдать код 304 и не посылать документ заново. HTTP/1.x 304 Not Modified Content-Encoding: gzip Etag: "453700005" Content-Type: text/javascript; charset=utf-8 Accept-Ranges: bytes Date: Tue, 15 Apr 2008 10:17:11 GMT

Альтернативный вариант - если документ изменился, тогда сервер просто посылает 200 с новым ETag .

Аналогичным образом работает связка Last-Modified + If-Modified-Since:

  • сервер посылает дату последней модификации в заголовке Last-Modified (вместо ETag)
  • браузер кеширует документ, и при следующем запросе того же документа посылает дату закешированной версии в заголовке If-Modified-Since (вместо If-None-Match)
  • сервер сверяет даты, и если документ не изменился - высылает только код 304, без содержимого.
  • Эти способы работают стабильно и хорошо, но браузеру в любом случае приходится делать по запросу для каждого скрипта или стиля.

    Умное кеширование. Версионность

    Общий подход для версионности - в двух словах:

  • Во все скрипты добавляется версия (или дата модификации). Например, http://сайт/my.js превратится в http://сайт/my.v1.2.js
  • Все скрипты жестко кешируются браузером
  • При обновлении скрипта версия меняется на новую: http://сайт/my.v2.0.js
  • Адрес изменился, поэтому браузер запросит и закеширует файл заново
  • Старая версия 1.2 постепенно выпадет из кеша
  • Жесткое кеширование

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

    Для этого достаточно добавить заголовки Expires и Cache-Control: max-age.

    Например, чтобы закешировать на 365 дней в PHP:

    Header("Expires: ".gmdate("D, d M Y H:i:s", time()+86400*365)." GMT"); header("Cache-Control: max-age="+86400*365);

    Или можно закешировать контент надолго, используя mod_header в Apache:

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

    Большинство браузеров (Opera, Internet Explorer 6+, Safari) НЕ кешируют документы, если в адресе есть вопросительный знак, т.к считают их динамическими.

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

    P.S А вот Firefox кеширует адреса с вопросительными знаками..

    Автоматическое преобразование имен

    Разберем, как автоматически и прозрачно менять версии, не переименовывая при этом сами файлы.

    Имя с версией -> Файл

    Самое простое - это превратить имя с версией в оригинальное имя файла.

    На уровне Apache это можно сделать mod_rewrite:

    RewriteEngine on RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 [L]

    Такое правило обрабатывает все css/js/gif/png/jpg-файлы, вырезая из имени версию.

    Например:

    /images/logo.v2.gif -> /images/logo.gif
    /css/style.v1.27.css -> /css/style.css
    /javascript/script.v6.js -> /javascript/script.js

    Но кроме вырезания версии - надо еще добавлять заголовки жесткого кеширования к файлам. Для этого используются директивы mod_header:

    Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" Header add "Cache-Control" "max-age=315360000"

    А все вместе реализует вот такой апачевый конфиг:

    RewriteEngine on # убирает версию, и заодно ставит переменную что файл версионный RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 # жестко кешируем версионные файлы Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE

    Из-за порядка работы модуля mod_rewrite, RewriteRule нужно поставить в основной конфигурационный файл httpd.conf или в подключаемые к нему(include) файлы, но ни в коем случае не в.htaccess , иначе команды Header будут запущены первыми, до того, как установлена переменная VERSIONED_FILE .

    Директивы Header могут быть где угодно, даже в.htaccess - без разницы.

    Автоматическое добавление версии в имя файла на HTML-странице

    Как ставить версию в имя скрипта - зависит от Вашей шаблонной системы и, вообще, способа добавлять скрипты (стили и т.п.).

    Например, при использовании даты модификации в качестве версии и шаблонизатора Smarty - ссылки можно ставить так:

    Функция version добавляет версию:

    Function smarty_version($args){ $stat = stat($GLOBALS["config"]["site_root"].$args["src"]); $version = $stat["mtime"]; echo preg_replace("!\.(+?)$!", ".v$version.\$1", $args["src"]); }

    Результат на странице:

    Оптимизация

    Чтобы избежать лишних вызовов stat , можно хранить массив со списком текущих версий в отдельной переменной

    $versions["css"] = array("group.css" => "1.1", "other.css" => "3.0", }

    В этом случае в HTML просто подставляется текущая версия из массива.

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

    Применимость

    Такой способ кеширования работает везде, включая Javascript, CSS, изображения, flash-ролики и т.п.

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

    Порой, бывает необходимо запрещать браузеру кэшировать страницу, так как информация на ней обновляется каждый раз. Это может быть генерация данных, соответственно выбранным фильтрам или другой контент, который каждый раз создается по-новому. Одним словом, бывают моменты, когда необходимо запретить коварной программе кэшировать страницу. Сегодня, мы узнаем, как реализовать это разными способами, с помощью PHP или HTML или.htaccess.

    Запрет кэширования страницы на HTML

    Сделать это можно с помощью мета тегов. Сейчас мы разберем разные варианты запрета на кэширование.

    Запрет на кэширование браузером и прокси-сервером

    Запрет кэширования страницы, только браузером

    Установка кэширования на определенное время, для браузера

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

    Установка кэширования на определенное время, для прокси-сервера

    Практически, то же самое, что и в предыдущем коде, только указание стоит конкретно для прокси-сервера.

    Запретить кэширование страницы с помощью PHP

    Практически, все тоже самое, что в случае с HTML, только информацию будем выводить через header заголовки. Вот, как реализовать абсолютный запрет на кэш:

    Также, можно разрешать кэшировать на определенное время. Например, разрешим кэширование только на 1 час.

    Запретить кэширование страницы с помощью.htaccess

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

    LoadModule expires_module modules/mod_expires.so LoadModule headers_module modules/mod_headers.so ... AddModule mod_expires.c AddModule mod_headers.c

    Теперь в файле.htaccess, собственно запрещаем кэшировать выводимые данные. Как нам известно, .htaccess файл будет распространяться на директорию, в которой лежит, и на все субдиректории.

    # Заголовок Cache-Control Header append Cache-Control "no-store, no-cache, must-revalidate" # Заголовок Expires ExpiresActive On ExpiresDefault "now"

    Важно заметить, что полный запрет кэширования, повышает нагрузку на сервер. Поэтому, играйтесь с этим осторожно! А лучше, установите определенное время, на которое можно кэшировать документы. Например, установим кэширование на 1 час:

    # Заголовок Cache-Control Header append Cache-Control "public" # Заголовок Expires ExpiresActive On ExpiresDefault "access plus 1 hours"

    Заключение

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

    Процесс довольно нудный и неудобный. Можно конечно выйти из ситуации, переименовывая каждый раз файлы, но опять же неудобно.

    Однако есть способ, который позволит остаться при прежних именах, и сбрасывать кеширование.css или.js файлов в тот момент, когда это будет нужно нам. И навсегда забыть о Ctrl + F5.

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

    Таким образом, запись в исходном коде теперь будет выглядеть так:

    Где 186485 - произвольная комбинация, которая выведет тот же файл, но браузер интерпретирует его как новый, благодаря псевдопараметру ?186485

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