Усовершенствованное кэширование страниц. Лучшие практики кэширования Кэширование css

  • 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». Это полезно для файлов, которые периодически изменяются.

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

Подавляющее большинство лучших практик кэширования относится к одному из двух паттернов:

Паттерн №1: неизменяемый контент и долгий max-age кэша Cache-Control: max-age=31536000
  • Содержимое по URL не меняется, следовательно…
  • Браузер или CDN могут без проблем закэшировать ресурс на год
  • Закэшированный контент, который младше, чем заданный max-age может использоваться без консультации с сервером

Страница: Эй, мне нужны "/script-v1.js" , "/styles-v1.css" и "/cats-v1.jpg" 10:24

Кэш: У меня пусто, как насчет тебя, Сервер? 10:24

Сервер: ОК, вот они. Кстати, Кэш, их стоит использовать в течение года, не больше. 10:25

Кэш: Спс! 10:25

Страница: Ура! 10:25

Следующий день

Страница: Эй, мне нужны "/script-v2 .js" , "/styles-v2 .css" и "/cats-v1.jpg" 08:14

Кэш: Картинка с котиками есть, остального нет. Сервер? 08:14

Сервер: Легко - вот новые CSS & JS. Еще раз, Кэш: их срок годности не больше года. 08:15

Кэш: Супер! 08:15

Страница: Спасибо! 08:15

Кэш: Хм, я не пользовался "/script-v1.js" & "/styles-v1.css" достаточно долго. Пора их удалять. 12:32

Используя этот паттерн, вы никогда не меняете контент определенного URL, вы меняете сам URL:

В каждом URL есть что-то, меняющееся одновременно с контентом. Это может быть номер версии, модифицированная дата или хэш контента (этот вариант я и выбрал для своего блога).

В большинстве серверных фреймворков есть инструменты, позволяющие с легкостью делать подобные вещи (в Django я использую Manifest​Static​Files​Storage); есть также совсем небольшие библиотеки в Node.js, решающие те же задачи, например, gulp-rev .

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

Паттерн №2: изменяемый контент, всегда проходящий ревалидацию на сервере Cache-Control: no-cache
  • Содержимое URL изменится, значит…
  • Любая локальная закэшированная версия не может использоваться без указания сервера.

Страница: Эй, мне нужно содержимое "/about/" и "/sw.js" 11:32

Кэш: Ничем не могу помочь. Сервер? 11:32

Сервер: Есть такие. Кэш, держи их при себе, но перед использованием спрашивай у меня. 11:33

Кэш: Так точно! 11:33

Страница: Спс! 11:33

На следующий день

Страница: Эй, мне опять нужно содержимое "/about/" и "/sw.js" 09:46

Кэш: Минутку. Сервер, с моими копиями все в порядке? Копия "/about/" лежит с понедельника, а "/sw.js" вчерашняя. 09:46

Сервер: "/sw.js" не менялась… 09:47

Кэш: Круто. Страница, держи "/sw.js" . 09:47

Сервер: …но "/about/" у меня новой версии. Кэш, держи ее, но как и в прошлый раз, не забудь сначала спросить меня. 09:47

Кэш: Понял! 09:47

Страница: Отлично! 09:47

Примечание: no-cache не значит “не кэшировать”, это значит “проверять” (или ревалидировать) закэшированный ресурс у сервера. А не кэшировать совсем браузеру приказывает no-store . Также и must-revalidate означает не обязательную ревалидацию, а то, что закэшированный ресурс используется только, если он младше, чем заданный max-age , и только в ином случае он ревалидируется. Вот так все запущено с ключевыми словами для кэширования.

В этом паттерне мы можете добавить к ответу ETag (идентификатор версии на ваш выбор) или заголовок Last-Modified . При следующем запросе содержимого со стороны клиента, выводится If-None-Match или If-Modified-Since соответственно, позволяя серверу сказать “Используй то, что у тебя есть, твой кэш актуален”, то есть вернуть HTTP 304.

Если отправка ETag / Last-Modified невозможна, сервер всегда отсылает содержимое полностью.

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

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

Использование max-age с изменяемым контентом это, как правило, неправильный выбор

И, к сожалению, он распространен, в качестве примера можно привести Github pages.

Представьте:

  • /article/
  • /styles.css
  • /script.js

С серверным заголовком:

Cache-Control: must-revalidate, max-age=600

  • Содержимое URL меняется
  • Если в браузере есть кэшированная версия свежее 10 минут, она используется без консультации с сервером
  • Если такого кэша нет, используется запрос к сети, по возможности с If- Modified-Since или If-None-Match

Страница: Эй, мне нужны "/article/" , "/script.js" и "/styles.css" 10:21

Кэш: У меня ничего нет, как у тебя, Сервер? 10:21

Сервер: Без проблем, вот они. Но запомни, Кэш: их можно использовать в течение ближайших 10 минут. 10:22

Кэш: Есть! 10:22

Страница: Спс! 10:22

Страница: Эй, мне опять нужны "/article/" , "/script.js" и "/styles.css" 10:28

Кэш: Упс, я извиняюсь, но я потерял "/styles.css" , но все остальное у меня есть, держи. Сервер, можешь подогнать мне "/styles.css" ? 10:28

Сервер: Легко, он уже изменился с тех пор, как ты в прошлый раз забирал его. Ближайшие 10 минут можешь смело его использовать. 10:29

Кэш: Без проблем. 10:29

Страница: Спасибо! Но, кажется, что-то пошло не так! Все поломалось! Что, вообще, происходит? 10:29

Этот паттерн имеет право на жизнь при тестировании, но ломает все в реальном проекте и его очень сложно отслеживать. В примере выше, сервер обновил HTML, CSS и JS, но выведена страница со старыми HTML и JS из кэша, к которым добавлен обновленный CSS с сервера. Несовпадение версий все портит.

Часто при внесении значительных изменений в HTML, мы меняем и CSS, для правильного отражения новой структуры, и JavaScript, чтобы и он не отставал от контента и стилей. Все эти ресурсы независимы, но заголовки кэширования не могут выразить это. В итоге у пользователей может оказаться последняя версия одного/двух ресурсов и старая версия остальных.

max-age задается относительно времени ответа, поэтому если все ресурсы передаются как часть одного адреса, их срок истечет одновременно, но и здесь сохраняется небольшой шанс рассинхронизации. Если у вас есть страницы, не включающие JavaScript или включающие другие стили, сроки годности их кэша будут рассинхронизированы. И хуже того, браузер постоянно вытаскивает содержимое из кэша, не зная, что HTML, CSS, & JS взаимозависимы, поэтому он с радостью может вытащить что-то одно из списка и забыть про все остальное. Учитывая все эти факторы вместе, вы должны понять, что вероятность появления несовпадающих версий достаточно велика.

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

К счастью, у пользователей есть запасной выход…

Обновление страницы иногда спасает

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

Сервис-воркер может продлить жизнь этих багов

Например, у вас есть такой сервис-воркер:

Const version = "2"; self.addEventListener("install", event => { event.waitUntil(caches.open(`static-${version}`) .then(cache => cache.addAll([ "/styles.css", "/script.js" ]))); }); self.addEventListener("activate", event => { // …delete old caches… }); self.addEventListener("fetch", event => { event.respondWith(caches.match(event.request) .then(response => response || fetch(event.request))); });

Этот сервис-воркер:

  • кэширует скрипт и стили
  • использует кэш при совпадении, иначе обращается к сети

Если мы меняем CSS/JS, мы также увеличиваем номер version , что инициирует обновление. Однако, так как addAll обращается сначала к кэшу, мы можем попасть в состояние гонки из-за max-age и несоответствующих версий CSS & JS.

После того как они закэшированы, у нас будут несовместимые CSS & JS до следующего обновления сервис-воркера - и это если мы опять не попадем при обновлении в состояние гонки.

Вы можете пропустить кэширование в сервис-воркере:

Self.addEventListener("install", event => { event.waitUntil(caches.open(`static-${version}`) .then(cache => cache.addAll([ new Request("/styles.css", { cache: "no-cache" }), new Request("/script.js", { cache: "no-cache" }) ]))); });

К сожалению, опции для кэширования не поддерживаются в Chrome/Opera и только-только добавлены в ночную сборку Firefox , но вы можете сделать это самостоятельно:

Self.addEventListener("install", event => { event.waitUntil(caches.open(`static-${version}`) .then(cache => Promise.all([ "/styles.css", "/script.js" ].map(url => { // cache-bust using a random query string return fetch(`${url}?${Math.random()}`).then(response => { // fail on 404, 500 etc if (!response.ok) throw Error("Not ok"); return cache.put(url, response); }) })))); });

В этом примере, я сбрасываю кэш с помощью случайного числа, но вы можете пойти дальше и добавлять хэш контента при сборке (это похоже на то, что делает sw-precache). Это своего рода реализация первого паттерна с помощью JavaScript, но работающая только с сервис-воркером, а не браузерами и CDN.

Сервис-воркеры и HTTP-кэш отлично работают вместе, не заставляйте их воевать!

Как видите, вы можете обойти ошибки с кэшированием в вашем сервис-воркере, но правильней будет решить корень проблемы. Правильная настройка кэширования не только облегчает работу сервис-воркера, но и помогает браузерам, не поддерживающим сервис-воркеры (Safari, IE/Edge), а также позволяет вам извлечь максимум из вашей CDN.

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

Const version = "23"; self.addEventListener("install", event => { event.waitUntil(caches.open(`static-${version}`) .then(cache => cache.addAll([ "/", "/script-f93bca2c.js", "/styles-a837cb1e.css", "/cats-0e9a2ef4.jpg" ]))); });

Здесь я закэшировал корневую страницу с паттерном №2 (серверная ревалидация) и все остальные ресурсы с паттерном №1 (неизменяемый контент). Каждое обновление сервис-воркера будет вызывать запрос к корневой странице, а все остальные ресурсы будут загружаться только, если их URL изменился. Это хорошо тем, что сохраняет трафик и улучшает производительность, независимо от того, обновляетесь ли вы с предыдущей или очень старой версии.

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

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

При аккуратном использовании max-age и изменяемый контент могут быть очень хороши

max-age очень часто бывает неправильным выбором для изменяемого контента, но не всегда. Например, у оригинала статьи max-age составляет три минуты. Состояние гонки не является проблемой, так как на странице нет зависимостей, использующих одинаковый паттерн кэширования (CSS, JS & изображения используют паттерн №1 - неизменяемый контент), все остальное этот паттерн не использует.

Этот паттерн означает, что я спокойно пишу популярную статью, а мой CDN (Cloudflare) может снять нагрузку с сервера, если, конечно, я готов подождать три минуты, пока обновленная статья станет доступной пользователям.

Этот паттерн надо использовать без фанатизма. Если я добавил новый раздел в статью, и поставил на него ссылку с другой статьи, я создал зависимость, которую надо разрешать. Пользователь может кликнуть на ссылку и получить копию статьи без искомого раздела. Если я хочу избежать этого, я должен обновить статью, удалить кэшированный вариант статьи с Cloudflare, подождать три минуты и только после этого добавлять ссылку в другую статью. Да, этот паттерн требует осторожности.

При правильном использовании кэширование дает значительное улучшение производительности и экономию трафика. Передавайте неизменяемый контент, если вы можете легко изменить URL, или используйте серверную ревалидацию. Смешивайте max-age и изменяемый контент, если вы достаточно смелы и уверены, что у вашего контента нет зависимостей, которые могут рассинхронизироваться.

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

Усовершенствованное кэширование страниц

Обратите внимание: IE10+, Chrome, Firefox, Opera и Safari имеют поддержку данной технологии.

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

Использование HTML5 кэширования дает следующие преимущества:

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

...Содержимое документа...

Декларирование HTML5 кэширования

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

Если данный атрибут не был задан на веб-странице и ссылка на нее отсутствует в файле кэширования, страница не будет кэширована.

Файл кэширования может иметь любое расширение (например.appcache или.mf), но обязан иметь специальный MIME тип: "text/cache-manifest".

В некоторых случаях веб-серверу может потребоваться дополнительная настройка, чтобы обслуживать данный MIME тип. Например, чтобы настроить веб-сервер Apache необходимо добавить следующий код в.htaccess файл:

AddType text/cache-manifest .appcache

Содержимое файла кэширования

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

Файл может содержать три секции:

  • CACHE MANIFEST - в данной секции указываются ссылки на файлы, которые необходимо кэшировать. Браузер будет автоматически кэшировать все перечисленные файлы сразу после первой загрузки.
  • NETWORK - в данной секции указываются файлы, которые требуют постоянного подключения к интернету. Браузер не будет кэшировать файлы перечисленные в данной секции.
  • FALLBACK - если файлы указанные в этой секции будут по какой-либо причине будут недоступны пользователи автоматически будут перенаправляться на другой указанный файл.

Секция CACHE MANIFEST обязательно должна присутствовать во всех файлах кэширования. Секции NETWORK и FALLBACK могут отсутствовать.

Пример файла кэширования:

CACHE MANIFEST #В данной секции перечислены файлы, которые будут кэшированы index.html flower.png NETWORK #Здесь перечислены файлы, которые требуют подключение к интернету login-page.php FALLBACK #Если mob.html недоступен пользователь будет перенаправлен на offline.html /mob.html /offline.html #Если какой-либо из HTML файлов недоступен пользователь будет перенаправлен на offline.html *.html /offline.html

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

Обратите внимание: некоторые браузеры могут иметь ограничение на размер кэшируемого содержимого на одном сайте.

Обновление кэшированных файлов

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

Для того, чтобы обновить кэшированое содержимое Вы должны сделать одно из следующих действий:

  • Очистить кэш в браузере пользователя
  • Обновить содержимое файла кэширования
  • Обновить кэш браузера программно (с помощью JavaScript)

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



Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: