Почему браузер не отправляет заголовок "If-None-Match"?

Я пытаюсь загрузить (и, надеюсь, кэшировать) динамически загружаемое изображение в PHP. Вот заголовки, отправленные и полученные:

Запрос:

GET /url:resource/Pomegranate/resources/images/logo.png HTTP/1.1
Host: pome.local
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Ubuntu Chromium/25.0.1364.160 Chrome/25.0.1364.160 Safari/537.22
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: PHPSESSID=fb8ghv9ti6v5s3ekkmvtacr9u5

Отклик:

HTTP/1.1 200 OK
Date: Tue, 09 Apr 2013 11:00:36 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.14 ZendServer/5.0
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Disposition: inline; filename="logo"
ETag: "1355829295"
Last-Modified: Tue, 18 Dec 2012 14:44:55 Asia/Tehran
Keep-Alive: timeout=5, max=98
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: image/png

Когда я перезагружаю URL, точно такие же заголовки отправляются и принимаются. Мой вопрос заключается в том, что я должен отправить в своем ответе, чтобы увидеть If-None-Match заголовок в последующем запросе?

ПРИМЕЧАНИЕ. Я считаю, что эти заголовки не так давно работали нормально, хотя я не уверен, но думаю, что браузеры изменены, чтобы не отправлять If-None-Match заголовок больше (я привык видеть этот заголовок). Я тестирую с Chrome и Firefox, и оба не отправляют заголовок.

3 ответа

Решение

Ты говоришь Cache-Control: no-store, no-cache - но ожидать кеширования?

Удалить эти значения (я думаю, must-revalidate, post-check=0, pre-check=0 могли / должны быть сохранены - они говорят браузеру, чтобы проверить с сервером, если было изменение).

И я бы придерживался Last-Modified один (если изменения в ваших ресурсах могут быть обнаружены с использованием только этого критерия) - ETag это более сложная вещь (особенно если вы хотите разобраться с ней в своем PHP-скрипте самостоятельно), и Google PageSpeed ​​/YSlow также советует против этого.

Та же проблема, аналогичное решение

Я пытался определить, почему Google Chrome не отправляет If-None-Match Заголовки при посещении сайта, который я разрабатываю. (Chrome 46.0.2490.71 м, хотя я не уверен, насколько актуальна версия.)

Это другой, хотя и очень похожий, ответ, чем в конечном счете цитируемый ФП (в комментарии относительно принятого ответа), но он решает ту же проблему:

Браузер не отправляет If-None-Match Заголовок в последующих запросах "когда это необходимо" (т. е. серверная логика, через PHP или аналогичная, использовалась для отправки ETag или же Last-Modified заголовок в первом ответе).

Предпосылки

Использование самозаверяющего сертификата TLS, который в Chrome становится красным, блокирует поведение кэширования Chrome. Прежде чем пытаться устранить проблему такого рода, установите самозаверяющий сертификат в эффективном доверенном корневом хранилище и полностью перезапустите браузер, как описано на /questions/15030/etagi-i-poslednie-izmeneniya-cherez-https-ssl/15039#15039.

1-е Богоявление: если-None-Match требует ETag от сервера, сначала

Я довольно быстро понял, что Chrome (и, вероятно, большинство или все другие браузеры) не будет отправлять If-None-Match заголовок, пока сервер уже не отправил ETag заголовок в ответ на предыдущий запрос. Логично, что это имеет смысл; в конце концов, как мог Chrome отправить If-None-Match когда ему никогда не давали значение?

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

Я использую PHP, поэтому комментарий @Mehran (OP) выскочил на меня (он / она говорит, что вызов header_remove() перед отправкой нужных кешей связанных заголовков решает проблему).

Откровенно говоря, я скептически отнесся к этому решению, потому что a) я был почти уверен, что PHP не будет отправлять какие-либо собственные заголовки по умолчанию (и это не так, учитывая мою конфигурацию); и б) когда я позвонил var_dump(headers_list()); незадолго до установки моих пользовательских кэширующих заголовков в PHP, единственным набором заголовков был тот, который я специально устанавливал чуть выше:

header('Content-type: application/javascript; charset=utf-8');

Итак, мне нечего терять, я попытался позвонить header_remove(); перед отправкой моих пользовательских заголовков. И к моему большому удивлению, PHP начал отправлять ETag заголовок вдруг!

2-е Богоявление: сжатый ответ меняет свой хэш

Затем я ударил меня, как мешок кирпичей: указав Content-type Заголовок в PHP, я говорил NGINX (веб-серверу, который я использую) GZIP ответ, как только PHP вернет его NGINX! Чтобы было ясно, Content-type то, что я указывал, было в списке типов NGINX для gzip.

Для простоты мои настройки NGINX GZIP следующие, а PHP подключен к NGINX через php-fpm:

gzip            on;
gzip_min_length 1;
gzip_proxied    expired no-cache no-store private auth;
gzip_types      text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;

gzip_vary on;

Я подумал, почему NGINX может удалить ETag который я отправил в PHP, когда указан тип содержимого "gzippable", и придумал очевидный теперь ответ: потому что NGINX изменяет тело ответа, которое PHP передает обратно, когда NGINX передает его! Это имеет смысл; нет смысла отправлять ETag когда он не будет соответствовать ответу, используемому для его генерации. Довольно приятно, что NGINX так разумно справляется с этим сценарием.

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

ОБНОВЛЕНИЕ: я нашел комментарий, который объясняет поведение NGINX в этом отношении, который, в свою очередь, приводит два ценных обсуждения по этому вопросу:

  1. NGINX ветка форума, обсуждающая поведение.
  2. Тангенциально связанная дискуссия в репозитории проекта; увидеть комментарий Posted on Jun 15, 2013 by Massive Bird,

В интересах сохранения этого ценного объяснения, если оно исчезнет, ​​я цитирую Massive Bird Вклад в обсуждение:

Nginx снимает Etag, когда получает ответ на лету. Это в соответствии со спецификацией, так как ответ без gzipped не является побайтным, сравнимым с ответом gzipped.

Тем не менее, поведение NGINX в этом отношении можно считать слегка ошибочным в том же

... также говорит, что есть вещь, называемая слабыми Etags (значение Etag с префиксом W/), и говорит нам, что его можно использовать для проверки того, является ли ответ семантически эквивалентным. В этом случае Nginx не должен связываться с этим. К сожалению, эта проверка так и не попала в дерево исходных текстов (к сожалению, цитата теперь заполнена спамом)."

Я не уверен относительно нынешнего настроя NGINX в этом отношении и, в частности, добавил ли он поддержку "слабых" Etags.

Итак, каково решение?

Итак, каково решение для получения ETag обратно в ответ? Выполните gzipping в PHP, чтобы NGINX увидел, что ответ уже сжат, и просто передает его, оставляя ETag заголовок не поврежден:

ob_start('ob_gzhandler');

Как только я добавил этот вызов до отправки заголовков и тела ответа, PHP начал отправлять ETag ценность с каждым ответом. Да!

Другие извлеченные уроки

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

Chrome и его панель инструментов разработчика "Net" ведут себя по-разному в зависимости от того, как инициируется запрос.

Если запрос "сделан свежим", например, нажатием Ctrl+F5, Chrome отправляет эти заголовки:

Cache-Control: no-cache
Pragma: no-cache

и сервер отвечает 200 OK,

Если запрос сделан только с F5, Chrome отправляет эти заголовки:

Pragma: no-cache

и сервер отвечает 304 Not Modified,

И наконец, если запрос сделан путем нажатия на ссылку на страницу, которую вы уже просматриваете, или размещения фокуса в адресной строке Chrome и нажатия Enter, Chrome отправляет следующие заголовки:

Cache-Control: no-cache
Pragma: no-cache

и сервер отвечает 200 OK (from cache),

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

Возможно, наиболее запутанным является то, что Chrome автоматически вставляет Cache-Control: no-cache а также Pragma: no-cache заголовки в исходящем запросе, когда фактически Chrome получает ответы из своего кэша (как показано в 200 OK (from cache) ответ).

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

Отправляя это для будущего меня...

У меня была похожая проблема, я отправлял ETag в ответе, но HTTP-клиент не отправлял If-None-Match заголовок в последующих запросах (что было странно, потому что это было накануне).

Оказывается, я использовал http://localhost:9000 для развития (который не использовал If-None-Match) - переключением на http://127.0.0.1:9000 Chrome1 автоматически начал отправку If-None-Match Заголовок в запросах снова.

Дополнительно - обеспечить Devtools > Network > Disable Cache [ ] не проверяется.

Chrome: версия 71.0.3578.98 (официальная сборка) (64-разрядная версия)

1 Я нигде не могу найти это задокументировано - я предполагаю, что Chrome был ответственен за эту логику.

Это случилось со мной по двум причинам:

  1. Мой сервер не отправил заголовок ответа etag. Я обновил мой jetty web.xml, чтобы вернуть etag, добавив следующее:

    <init-param>
        <param-name>etags</param-name>
        <param-value>true</param-value>
    </init-param>
    
  2. URL, который я назвал, был для файла XML. Когда я изменил его на HTML-файл, Chrome начал отправлять заголовок "if-none-match"!

Я надеюсь, что это помогает кому-то

Аналогичная проблема

Я пытался получить условный запрос GET с помощью If-None-Match заголовок, предоставив надлежащий Etag заголовок, но безрезультатно ни в одном браузере я пробовал.

После долгих испытаний я понимаю, что браузеры обрабатывают оба GET а также POST по тому же пути, что и тот же кандидат в кэш. Таким образом, имея GET с надлежащим Etag был фактически отменен с немедленным "POST" на тот же путь с Cache-Control:"no-cache, private"даже если он был предоставлен X-Requested-With:"XMLHttpRequest",

Надеюсь, что это может быть полезно для кого-то.

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

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

Исправление, которое решило проблему.

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