Кеширование на стороне клиента
Если я правильно понимаю, браузер использует кеширование изображений, JS файлов и т.д. на основе имени файла. Таким образом, существует опасность, что если один такой файл будет обновлен (на сервере), браузер будет использовать вместо него кешированную копию.
Обходной путь для этой проблемы состоит в том, чтобы переименовать все файлы (как часть сборки), так что имя файла включает в себя хеш файл MD5 его содержимого, например
foo.js -> foo_AS577688BC87654.js
me.png -> me_32126A88BC3456BB.png
Однако, помимо переименования самих файлов, все ссылки на эти файлы должны быть изменены. Для exmaple тег, такой как <img src="me.png"/>
, должен быть изменен на <img src="me_32126A88BC3456BB.png"/>
.
Очевидно, что это может стать довольно сложным, особенно если учесть, что ссылки на эти файлы могут быть динамически созданы внутри серверного кода.
Конечно, одним из решений является полное отключение кэширования в браузере (и любые кеши между сервером и браузером) с использованием HTTP-заголовков. Однако отсутствие кэширования создаст собственный набор проблем.
Есть ли лучшее решение?
Спасибо,
Дон
Ответы
Ответ 1
Лучшее решение, похоже, относится к именам файлов версий, добавляя последнее модифицированное время.
Вы можете сделать это следующим образом: добавьте правило перезаписи в вашу конфигурацию Apache, например:
RewriteRule ^(.+)\.(.+)\.(js|css|jpg|png|gif)$ $1.$3
Это перенаправит любой "версированный" URL-адрес на "обычный". Идея состоит в том, чтобы сохранить ваши имена файлов одинаковыми, но извлечь выгоду из кеша. Решение добавить параметр к URL-адресу не будет оптимальным с некоторыми прокси-серверами, которые не кэшируют URL-адреса с параметрами.
Затем вместо записи:
<img src="image.png" />
Просто вызовите функцию PHP:
<img src="<?php versionFile('image.png'); ?>" />
С версией versionFile() выглядит следующим образом:
function versionFile($file){
$path = pathinfo($file);
$ver = '.'.filemtime($_SERVER['DOCUMENT_ROOT'].$file).'.';
echo $path['dirname'].'/'.str_replace('.', $ver, $path['basename']);
}
И это! Браузер попросит image.123456789.png, Apache перенаправит это на image.png, так что вы будете использовать кеш во всех случаях и не будете иметь устаревшей проблемы, не имея необходимости беспокоиться о версиях имени файла.
Вы можете увидеть подробное объяснение этой техники здесь: http://particletree.com/notebook/automatically-version-your-css-and-javascript-files/
Ответ 2
Почему бы просто не добавить номер версии "querystring" и обновлять версию каждый раз?
foo.js → foo.js? version = 5
Во время сборки все еще немного работы, чтобы обновлять номера версий, но имена файлов не нужно изменять.
Ответ 3
Переименование ваших ресурсов - это путь, хотя мы используем номер сборки и вставляем это в имя файла вместо хеша MD5
foo.js -> foo.123.js
так как это означает, что все ваши ресурсы могут быть переименованы детерминированным способом и разрешены во время выполнения.
Затем мы используем настраиваемые элементы управления для создания ссылок на ресурсы при загрузке страницы на основе номера сборки, который хранится в настройках приложения.
Ответ 4
Мы следовали аналогичной схеме с PJP, используя Rails и Nginx.
Мы хотели, чтобы изображения аватара пользователя были кэшированы браузером, но при смене аватара нам понадобилось, чтобы кеш был недействительным как можно скорее.
Мы добавили метод к модели аватара, чтобы добавить временную метку к имени файла:
return "/images/#{sourcedir}/#{user.login}-#{self.updated_at.to_s(:flat_string)}.png"
Во всех местах кода, где использовались аватары, мы ссылались на этот метод, а не на URL. В конфигурации Nginx мы добавили эту переписку:
rewrite "^/images/avatars/(.+)-[\d]{12}.png" /images/avatars/$1.png;
rewrite "^/images/small-avatars/(.+)-[\d]{12}.png" /images/small-avatars/$1.png;
Это означало, что если файл изменился, его URL-адрес в HTML изменился, поэтому пользовательский браузер сделал новый запрос для файла. Когда запрос достиг Nginx, он был переписан на простое имя файла.
Ответ 5
Я бы предложил использовать кэширование ETags в этой ситуации, см. http://en.wikipedia.org/wiki/HTTP_ETag. Затем вы можете использовать хеш в качестве этага. Запрос будет по-прежнему отправляться на каждый ресурс, но браузер будет загружать только те элементы, которые были изменены с момента последней загрузки.
Прочитайте в своих документах веб-сервера/платформы о том, как правильно использовать etags, у большинства достойных платформ есть встроенная поддержка.
Ответ 6
Большинство современных браузеров проверяют заголовок if-modified-since всякий раз, когда кешируемый ресурс находится в HTTP-запросе. Однако не все браузеры поддерживают заголовок if-modified-since.
Существует три способа "заставить" браузер загружать кешированный ресурс.
Вариант 1 Создайте строку запроса с версией #. src="script.js?ver=21"
. Недостатком является то, что многие прокси-серверы не кэшируют ресурс с помощью строк запроса. Он также требует обновления сайта для изменений.
Вариант 2 Создайте систему имен для ваших файлов src="script083010.js"
. Однако недостатком варианта 1 является то, что для этого также требуются обновления на сайте при каждом изменении файла.
Вариант 3 Возможно, самое изящное решение, просто настройте заголовки кеширования: последний раз модифицирован и истекает на вашем сервере. Главным недостатком этого является то, что пользователям, возможно, придется повторно использовать ресурсы, поскольку они истекли, но не изменились. Кроме того, заголовок с последним изменением не работает, когда контент обслуживается с нескольких серверов.
Вот несколько ресурсов для проверки: Yahoo Google AskApache.com
Ответ 7
Это действительно проблема только в том случае, если ваш веб-сервер устанавливает длинный заголовок "Expires" (что-то вроде ExpiresDefault "access plus 10 years"
в вашей конфигурации Apache). В противном случае браузер сделает условное GET, основанное на измененном времени и/или Etag. Вы можете проверить, что происходит на вашем сайте, с помощью веб-прокси или расширения, такого как Firebug (на панели Net). В вашем вопросе не упоминается, как настроен ваш веб-сервер, и какие заголовки он отправляет со статическими файлами.
Если вы не устанавливаете заголовок Expires будущего будущего, вам нечего делать. Ваш веб-сервер обычно обрабатывает условные GET для статических файлов на основе последнего измененного времени. Если вы задаете длинный заголовок Expires, тогда да, вам нужно добавить какую-то версию в имя файла, например, ваш вопрос, а остальные ответы уже упомянуты.
Ответ 8
Я также думал об этом для сайта, на котором я поддерживаю, где было бы большой работой по изменению всех ссылок. У меня есть две идеи:
1.
Установите удаленные заголовки истечения кеша и примените изменения, которые вы предлагаете для наиболее часто загружаемых файлов. Для других файлов устанавливаются заголовки, поэтому они истекают через очень короткое время - например. 10 минут. Тогда, если у вас есть 10-минутное время простоя при обновлении приложения, кеши будут обновляться, когда пользователи перейдут на сайт. Общая навигация по сайту должна быть улучшена, так как файлы будут загружаться каждые 10 минут не каждый раз.
2.
Каждый раз, когда новая версия приложения развертывается в другом контексте, который содержит номер версии. например. www.site.com/app_2_6_0/Я не уверен в этом, так как пользовательские закладки будут разбиты на каждое обновление.
Ответ 9
Я считаю, что комбинация решений работает лучше всего:
-
Устанавливать даты истечения срока действия кеша для каждого типа ресурса (изображение, страница и т.д.) соответственно для этого ресурса, например:
- Ваши статические страницы "О себе", "Контакт" и т.д., вероятно, не изменятся более чем раз в год, поэтому вы можете легко положить время кеша на месяц на этих страницах.
- Изображения, используемые на этих страницах, могут иметь вечное время кеширования, так как вы более похожи, чтобы заменить изображение, а затем изменить его.
- Изображение аватаров может иметь срок годности.
-
Некоторым ресурсам нужны измененные даты в их именах. Например, аватары, сгенерированные изображения и т.д.
-
Некоторые вещи никогда не должны быть кешами, новыми страницами, пользовательским контентом и т.д. В этих случаях вы должны кэшировать на сервере, но никогда на стороне клиента.
В конце концов вам нужно внимательно рассмотреть каждый тип ресурса, чтобы определить, какое время кеша указывать браузеру, и всегда быть консервативным, если вы не уверены. Вы можете увеличить время позже, но гораздо больнее что-то уловить.
Ответ 10
Возможно, вы захотите проверить подход, сделанный плагином "uiperformance", который вы можете найти здесь здесь. Он делает много вещей, о которых вы упоминаете, но автоматизирует их (задает время истечения срока в течение длительного времени, а затем увеличивает номера версий при изменении файлов).
Итак, если вы используете grails, вы получите это бесплатно. Если вы этого не сделаете - возможно, вы сможете одолжить применяемые методы.
Также - заимствованную страницу ui-performance, - прочитайте следующий 14 правил.
Ответ 11
ETags, по-видимому, обеспечивают решение для этого...
По http://httpd.apache.org/docs/2.0/mod/core.html#fileetag мы можем настроить браузер для генерации ETags на размер файла (вместо time/inode/etc). Это поколение должно быть постоянным при развертывании нескольких серверов.
Просто включите его (/etc/apache2/apache2.conf)
FileETag Size
& вы должны быть хорошими!
Таким образом, вы можете просто ссылаться на свои изображения как <img src='/path/to/foo.png' />
и по-прежнему использовать все преимущества кеширования HTTP.