Я пытаюсь загрузить (и, надеюсь, кэш) динамически загруженное изображение в PHP. Вот отправленные и полученные заголовки:
Когда я перезагружаю URL-адрес, то точно такие же заголовки отправляются и принимаются. Мой вопрос в том, что я должен отправить в свой ответ, чтобы увидеть заголовок If-None-Match
в последующем запросе?
ПРИМЕЧАНИЕ. Я считаю, что эти заголовки делались совсем недавно, хотя я не могу быть уверен, но я думаю, что браузеры изменились, чтобы больше не отправлять заголовок If-None-Match
(я видел этот заголовок). Я тестирую Chrome и Firefox, и оба не могут отправить заголовок.
Ответ 2
Та же проблема, аналогичное решение
Я пытался определить, почему Google Chrome не будет отправлять заголовки If-None-Match
при посещении сайта, который я разрабатываю. (Chrome 46.0.2490.71 м, хотя я не уверен, насколько актуальна версия.)
Это другой, хотя и очень похожий, ответ, чем в конечном счете цитируемый ФП (в комментарии относительно принятого ответа), но он решает ту же проблему:
Браузер не отправляет заголовок If-None-Match
в последующих запросах "когда должен" (т. ETag
Логика на стороне сервера, через PHP или аналогичная, использовалась для отправки ETag
или Last-Modified
в первом ответе),
Предпосылки
Использование самозаверяющего сертификата TLS, который в Chrome окрашивает блокировку в красный цвет, изменяет поведение кэширования Chrome. Прежде чем пытаться устранить проблему такого рода, установите самозаверяющий сертификат в эффективном доверенном корневом хранилище и полностью перезапустите браузер, как описано на fooobar.com/questions/794485/....
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 в этом отношении, который, в свою очередь, приводит два ценных обсуждения по этому вопросу:
- NGINX ветка форума, обсуждающая поведение.
- Тангенциально связанная дискуссия в репозитории проекта; см. комментарий
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)
).
Этот опыт был довольно информативным для меня, и я надеюсь, что другие найдут этот анализ ценности в будущем.