Как узнать, когда отправлять ответ 304 Not Modified

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

  1. Какие окончательные HTTP-заголовки мне нужно проверить, чтобы точно знать, следует ли мне отправлять ответ 304, и что я ищу, когда проверяю их?

  2. Кроме того, есть ли какие-либо заголовки, которые мне нужно отправить при первоначальной отправке файла (например, «Last-Modified») в качестве ответа 200?

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


А что насчет заголовка управления кешем? Могут ли различные возможные значения этого влиять на то, что вы отправляете клиенту (а именно, max-age), или должны выполняться только if-modified-Since?

Я просто хотел бы добавить, что при отправке ответа 304 вы должны отправлять только заголовок, а не контент.

GateKiller 07.08.2008 12:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
1
4 197
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

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

См. Соответствующий раздел в RFC в http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25.

Ответ принят как подходящий

Вот как я это реализовал. Код работает чуть больше года и с несколькими браузерами, поэтому я считаю его довольно надежным. Это основано на RFC 2616 и на наблюдении за тем, что и когда отправляли различные браузеры.

Вот псевдокод:

server_etag = gen_etag_for_this_file(myfile)
etag_from_browser = get_header("Etag")

if etag_from_browser does not exist:
    etag_from_browser = get_header("If-None-Match")
if the browser has quoted the etag:
    strip the quotes (e.g. "foo" --> foo)

set server_etag into http header

if etag_from_browser matches server_etag
    send 304 return code to browser

Вот фрагмент моей серверной логики, которая справляется с этим.

/* the client should set either Etag or If-None-Match */
/* some clients quote the parm, strip quotes if so    */
mketag(etag, &sb);

etagin = apr_table_get(r->headers_in, "Etag");
if (etagin == NULL)
    etagin = apr_table_get(r->headers_in, "If-None-Match");
if (etag != NULL && etag[0] == '"') {
    int sl; 
    sl = strlen(etag);
    memmove(etag, etag+1, sl+1);
    etag[sl-2] = 0;
    logit(2,"etag=:%s:",etag);
}   
... 
apr_table_add(r->headers_out, "ETag", etag);
... 
if (etagin != NULL && strcmp(etagin, etag) == 0) {
    /* if the etag matches, we return a 304 */
    rc = HTTP_NOT_MODIFIED;
}   

Если вам нужна помощь с генерацией etag, напишите еще один вопрос, и я найду код, который тоже делает это. HTH!

Вы действительно сталкивались с клиентами, которые отправляют в запросе заголовок ETag? Его следует использовать только в ответах. Также его всегда следует указывать в соответствии со спецификацией.

Matt Kantor 17.09.2012 04:08

Мэтт, клиент отправляет обратно полученный ранее etag, поэтому сервер может решить, является ли HTTP_NOT_MODIFIED подходящим ответом. Моя основная работа в то время была связана с Firefox и Safari; они оба будут включать сохраненный etag, если предыдущий запрос для данного ресурса дал etag клиенту. Посмотрите здесь "Типичное использование": en.wikipedia.org/wiki/HTTP_ETag

Mark Harrison 17.09.2012 05:39

Клиент включает свои метки в заголовки If-None-Match / If-Match. Я никогда не видел, чтобы UA отправлял заголовок ETag: "..." в запросе, а?

Matt Kantor 17.09.2012 07:22

Хм, мне трудно вспомнить точный код, который был написан лет 4-5 назад. Согласно моему псевдокоду выше, похоже, что я проверял как ETag, так и If-None-Match. Может, проверка ETag бесполезна?

Mark Harrison 17.09.2012 07:38

относительно кеш-контроля:

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

Ответ 304 Not Modified может быть результатом запроса GET или HEAD с заголовком If-Modified-Since («IMS») или If-Not-Match («INM»).

Чтобы решить, что делать при получении этих заголовков, представьте, что вы обрабатываете запрос GET без этих условных заголовков. Определите, какими будут значения ваших заголовков ETag и Last-Modified в этом ответе, и используйте их для принятия решения. Надеюсь, вы построили свою систему так, что ее определение обходится дешевле, чем построение полного ответа.

Если есть INM и значение этого заголовка совпадает со значением, которое вы поместили бы в ETag, тогда ответьте 304.

Если есть IMS и значение даты в этом заголовке позже, чем то, которое вы поместили бы в Last-Modified, тогда ответьте 304.

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

Чтобы решить часть 2 вашего вопроса с наименьшими усилиями, выясните, какой из заголовков (Expires, ETag и Last-Modified) вы можете легко и правильно создать в своем веб-приложении.

Предлагаемые материалы для чтения:

http://www.w3.org/Protocols/rfc2616/rfc2616.html

http://www.mnot.net/cache_docs/

Мы также обрабатываем кэшированные, но защищенные ресурсы. Если вы отправляете / генерируете заголовок ETAg (который СЛЕДУЕТ вам в разделе 13.3 RFC 2616), то клиент ДОЛЖЕН использовать его в условном запросе (обычно в заголовке If-None-Match - HTTP_IF_NONE_MATCH -). Если вы отправляете заголовок Last-Modified (опять же, ОБЯЗАТЕЛЬНО), вы должны проверить заголовок If-Modified-Since - HTTP_IF_MODIFIED_SINCE -. Если вы отправляете оба, клиент ДОЛЖЕН отправить оба, но он ДОЛЖЕН отправить ETag. Также обратите внимание, что валидация просто определяется как проверка условных заголовков на строгое равенство с теми, которые вы отправляете. Кроме того, только сильный валидатор (такой как ETag) будет использоваться для ранжированных запросов (когда запрашивается только часть ресурса).

На практике, поскольку ресурсы, которые мы защищаем, довольно статичны и допустима задержка в одну секунду, мы делаем следующее:

  1. Проверьте, авторизован ли пользователь для доступа к запрошенному ресурсу.

    Если это не так, перенаправьте их или отправьте ответ 4xx в зависимости от ситуации. Мы сгенерируем 404 ответа на запросы, которые выглядят как попытки взлома или явные попытки выполнить завершающий запуск безопасности.

  2. Сравните заголовок If-Modified-Since с заголовком Last-Modified, который мы отправили бы (см. Ниже) для строгого равенства

    Если они совпадают, отправьте ответ 304 Not Modified и завершите обработку страницы.

  3. Создайте заголовок Last-Modified, используя время модификации запрошенного ресурса

    Найдите формат даты HTTP в RFC 2616

  4. Отправьте заголовок и содержимое ресурса вместе с соответствующим Content-Type

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

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

Другие вопросы по теме