Заголовки HTTP: управление кэшем и механизмом истории - PullRequest
12 голосов
/ 27 июня 2011

Я пытаюсь найти лучшие HTTP-заголовки для отправки для четырех вариантов использования.Я надеюсь придумать заголовки, которые не зависят от сниффинга версии агента пользователя / протокола, но я приму это, если больше ничего не подходит.Все URL-адреса выбираются через полностью настраиваемый обработчик, поэтому я могу выбирать все заголовки, как мне нравится, , это все о промежуточных прокси и пользовательских агентах .Если возможно, это должно быть совместимо с клиентами HTTP / 1.0 и HTTP / 1.1.Если существует несколько решений, наилучшее из них будет самым коротким при отправке по сети.

Статическое общедоступное содержимое

Все «Статическое общедоступное содержимое» - это материал, который HTTPна самом деле все о: если URL-адрес один и тот же, содержание то же самое.Я могу сделать это легко: например, я поместил значок профиля пользователя в http://domain.com/profiles/xyz/icon/1234abcd, где «1234abcd» - это SHA-1 содержимого файла значка.Если в будущем я перейду на значок, я создам новый URL-адрес и изменю все существующие источники ссылок, которые должны использовать новый значок.Каковы лучшие заголовки, чтобы объявить, что это может быть кэшировано навсегда и может быть разделено?В настоящее время я думаю что-то вроде:

Date: <current time>
Expires: <current time + one year>

Этого достаточно, чтобы разрешить кэширование агентами пользователя и прокси-серверами?Нужно ли мне Last-Modified или Pragma?

Статический непубличный контент

Весь «Статический непубличный контент» - это материал, который статичен, но может и не бытьбыть доступным для всех.Фактически, этот контент будет доступен только выбранным вошедшим в систему пользователям (сеанс сохраняется с использованием cookie-файла сеанса, содержащего UUID сеанса).Если URL-адрес один и тот же, содержимое остается тем же.Тем не менее, ответ не является публичным.Вариант использования может быть изображением, предоставленным избранным друзьям в социальной сети.В настоящее время я думаю о чем-то вроде:

Date: <current time>
Expires: <current time>
Cache-Control: private, max-age=<huge number>, s-maxage=0

Этого достаточно, чтобы разрешить кэширование пользовательскими агентами и отключить прокси?Нужно ли мне Pragma?

Изменчивый общедоступный контент

Весь «Изменчивый общедоступный контент» - это материал, который является изменчивым и доступным для всех.Что-то вроде титульной страницы http://slashdot.org/, когда вы не вошли в систему. Цель состоит в том, чтобы позволить быстро обновлять контент в неизменном URL-адресе. Обратите внимание, что я НЕ хочу нарушать механизм истории пользовательского агента (то есть нажатие чего-либо с изменчивой страницы и затем нажатие кнопки назад не должно приводить к извлечению изменчивой страницы с сервера - однако,щелкнув ссылку, ведущую на первую страницу, следует получить ресурс с сервера).В настоящее время я думаю что-то вроде:

Date: <current time>
Expires: <current time>
Cache-Control: public, max-age=0, s-maxage=0

Этого достаточно, чтобы предотвратить кеширование, но разрешить механизм истории (кнопка назад)?Я знаю, что если я отправлю Cache-Control: no-store, must-revalidate, я могу принудительно перезагрузить, но это не то, что я хочу, потому что это также сломает кнопку возврата.Нужно ли мне Last-Modified или Pragma?

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

Волатильнонепубличный контент

Весь «непостоянный непубличный контент» - это материал, который непостоянен и доступен не всем (частным лицам).Что-то вроде титульной страницы http://slashdot.org/, когда вы вошли в систему. Цель состоит в том, чтобы позволить быстро обновлять контент в неизменном URL-адресе. Обратите внимание, что я НЕ хочу нарушать механизм истории пользовательского агента (то есть нажатие чего-либо с изменчивой страницы и затем нажатие кнопки назад не должно приводить к извлечению изменчивой страницы с сервера - однако,щелкнув ссылку, ведущую на первую страницу, следует получить ресурс с сервера).В настоящее время я думаю о чем-то вроде:

Date: <current time>
Expires: <current time>
Cache-Control: private, max-age=0, s-maxage=0

Этого достаточно, чтобы предотвратить кэширование, но разрешить механизм истории (кнопка назад)?Нужно ли Pragma?


Вещи, которые все еще нуждаются в тестировании с моими предлагаемыми заголовками:

  • Убедитесь, что частный контент не будет пропущен через прокси HTTP / 1.0.
  • Убедитесь, что кэширование работает правильно в прокси.
  • Убедитесь, что кэширование работает правильно в пользовательских агентах.
  • Убедитесь, что механизм истории пользовательских агентов работает в пользовательских агентах (все случаи).
  • Убедитесь, что по ссылке на изменчивую страницу происходит выборка свежего контента с сервера.
  • Проверьте все результаты при использовании HTTPS вместо HTTP.

Ответы [ 2 ]

10 голосов
/ 29 июня 2011

Я отвечу на свой вопрос:

Статическое общедоступное содержимое

Date: <current time>
Expires: <current time + one year>

Обоснование: Это совместимо с прокси HTTP / 1.0 и RFC 2616 Section14: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21 Заголовок Last-Modified не требуется для правильного кэширования (поскольку соответствующие пользовательские агенты следуют за заголовком Expires), но может быть включен для потребления конечным пользователем.Включение заголовка Last-Modified также может уменьшить передачу данных с сервера в случае нажатия пользователем кнопки «Обновить / Обновить».Если добавлен заголовок Last-Modified, он должен отражать реальные данные, а не что-то придуманное.Если вы хотите уменьшить передачу данных с сервера (в случае, если пользователь нажимает кнопку Обновить / Обновить) и не можете включить действительный заголовок Last-Modified, вы можете добавить заголовок ETag, чтобы разрешить условный GET (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26). Если вы уже включили Last-Modified также добавление ETag - просто трата. Обратите внимание, что Last-Modified явно лучше, потому что поддерживается также клиентами и прокси HTTP / 1.0. Подходящим значением для ETag в случае динамических страниц является SHA-1 изсодержимое страницы / ресурса. Обратите внимание, что использование Last-Modified или ETag не поможет с нагрузкой на сервер, только с исходящим интернет-каналом / скоростью передачи данных на сервере.

Статический непубличныйcontent

Date: <current time>
Expires: <current time>
Cache-Control: private, max-age=31536000, s-maxage=0
Vary: Cookie

Обоснование: заголовки Date и Expires предназначены для совместимости с HTTP / 1.0, и поскольку нет разумного способа указать, что ответ является частным, эти заголовки сообщают, что ответможет не кэшироваться. Заголовок Cache-Control сообщает, что этот ответ может кэшироваться частным кешем, но общий кэш может не кэшировать ответ. s-maxage=0 iдобавлено потому, что private может поддерживаться не всеми прокси, поддерживающими Cache-Control (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 - я не знаю, какие прокси не работают).Для max-age установлено значение 60*60*24*365 (1 год), поскольку спецификация HTTP / 1.1 не определяет какой-либо верхний предел для этого параметра, я полагаю, что это зависит от реализации.Заголовки Expires ДОЛЖНЫ быть ограничены одним годом в будущем, поэтому использование той же логики здесь должно быть в порядке.Заголовок Vary: Cookie необходим, потому что сеанс, используемый для проверки того, разрешено ли посетителю просматривать содержимое, передается в cookie-файле;поскольку возвращаемый ответ зависит от значения cookie, кеш может не использовать кэшированный ответ при изменении заголовка cookie.

Я мог бы лично разбить последнюю часть.Не включая заголовок Vary: Cookie, я могу значительно улучшить кэширование.Например: у меня есть изображение профиля на http://domain.com/icon/12, которое возвращается только для выбранных аутентифицированных пользователей.У меня есть посетитель X с идентификатором сеанса 5f2, и я разрешаю изображение этому пользователю.Посетитель X выходит из системы, а затем снова входит в систему.Теперь X имеет идентификатор сеанса 2e8, сохраненный в его файле cookie сеанса.Если у меня Vary: cookie, пользовательский агент X не может использовать кэшированный образ и вынужден перезагрузить его в свой кэш.Поскольку содержимое зависит от Cookie, нельзя использовать условное GET с последним временем модификации.Я не проверял, может ли использование ETag помочь в этом случае, потому что в этом случае ответ сервера будет таким же (соответствует SHA-1 ETag, вычисленному из содержимого ответа).Имейте в виду, что Internet Explorer (по крайней мере до версии 9) всегда вызывает условный GET для ресурсов, которые включают Vary: Cookie (источник: http://blogs.msdn.com/b/ie/archive/2010/07/14/caching-improvements-in-internet-explorer-9.aspx).). Это связано с тем, что реализация внутреннего кэша MSIE не помнит, какой Cookie он отправил первымвремя, поэтому он не может знать, является ли текущий Cookie тем же самым.

Однако, вот пример проблемы, которая вызвана удалением заголовка Vary: Cookie, чтобы показать, почему это действительно требуется для технически правильного поведения. Посмотрите приведенный выше пример и представьте, что после выхода X из системы посетитель Y входит в систему с тем же пользовательским агентом (возможно, пользовательский агент был перезапущен между X и Y, это не имеет значения). Если Y просматривает страницу, содержащую ссылку на http://domain.com/icon/12, то Y увидит значок, встроенный в страницу, даже если Y не сможет увидеть значок, если X ранее не использовал тот же пользовательский агент. В моем случае я не считаю это достаточно большой проблемой, потому что Y мог бы получить доступ к значку вручную, проверяя кэш пользовательского агента независимо от того, возможно ли добавлено Vary: Cookie. Однако эта проблема может помешать Y заметить, что он технически не будет иметь доступа к этому контенту (это может быть важно, например, если Y является соавтором контента). Если содержимое считается конфиденциальным, сервер должен отправить no-store независимо от проблем, вызванных этой директивой Cache-Control.

Здесь также добавление заголовка Last-Modified поможет пользователям нажимать кнопку Обновить / Обновить (см. Обсуждение выше).

Изменчивый публичный контент

Date: <current time>
Expires: <current time>
Cache-Control: public, max-age=0, s-maxage=0
Last-Modified: <real-last-modification-time>

Обоснование. Сообщите клиентам и прокси-серверам HTTP / 1.0, что этот ответ следует немедленно считать устаревшим. Время Last-Modified включено, чтобы разрешить пропуск передачи данных контента, когда к ресурсу снова обращаются, и клиент поддерживает условное GET. Если Last-Modified нельзя использовать, ETag может использоваться в качестве замены (см. Обсуждение выше). Крайне важно использовать Last-Modified, чтобы разрешить условное GET для клиентов, совместимых с HTTP / 1.0.

Если содержимое может быть задержано даже незначительно, тогда Expires, max-age и s-maxage [sic] должны быть соответствующим образом скорректированы. Например, добавление 5 секунд к ним может очень помочь для очень популярного сайта, как предполагает ответ symcbean. Обратите внимание, что в отличие от условного GET, увеличение срока действия приведет к снижению нагрузки на сервер, а не просто к уменьшению исходящего трафика данных сервера (поскольку сервер будет видеть меньше запросов в общей сложности).

Изменчивый непубличный контент

Date: <current time>
Expires: <current time>
Cache-Control: private, max-age=0, s-maxage=0
Last-Modified: <real-last-modification-time>
Vary: Cookie

Обоснование. Сообщите клиентам и прокси-серверам HTTP / 1.0, что этот ответ следует немедленно считать устаревшим. Время Last-Modified включено, чтобы разрешить пропуск передачи данных контента, когда к ресурсу снова обращаются, и клиент поддерживает условное GET. Если Last-Modified нельзя использовать, ETag может использоваться в качестве замены (см. Обсуждение выше). Крайне важно использовать Last-Modified, чтобы разрешить условный GET для клиентов, совместимых с HTTP / 1.0. Также обратите внимание, что Cache-Control не должно включать no-cache, must-revalidate или no-store, поскольку использование любой из этих директив приведет к поломке кнопки возврата по крайней мере в одном пользовательском агенте. Однако, если контент, который передает сервер, содержит конфиденциальный материал, который не должен храниться в постоянном хранилище, флаг no-store ДОЛЖЕН использоваться независимо от нажатия кнопки «Назад». Предупреждение: обратите внимание, что использование no-store не может предотвратить попадание конфиденциального материала на жесткий диск без шифрования, если в операционной системе включена подкачка и подкачка не зашифрована! Также обратите внимание, что использование no-store имеет мало смысла, если соединение не зашифровано (HTTPS / SSL).

2 голосов
/ 27 июня 2011

В основном все в порядке, однако вам следует помнить, что прокси-серверы HTTP / 1.0 могут кэшировать контент, обслуживаемый как

Cache-Control: private

Таким образом, вы должны установить явный заголовок с измененной датой, а также заголовок expires.

Для вашего «Статического непубличного контента» вы должны добавить заголовок «Varies: Cookie».

Для вашего «изменчивого публичного контента»: как быстро он меняется? Установка TTL равной +5 секунд может отнять много усилий от ваших серверов.

Для 'Volatile непубличного контента' вы, вероятно, должны добавить no-cache, must-revalidate в заголовок Cache-control.

Заголовки Pragma, выдаваемые с сервера, не должны влиять ни на клиентов, ни на прокси.

Проверьте, что происходит, когда истекает срок действия вашего кэша (IME, вы можете получить систему, которая работает медленнее, чем система, к которой не обращаются, из-за всех условных запросов / 304 ответов)

...