为什么浏览器不发送“If-None-Match”标题?

时间:2013-04-09 11:23:42

标签: http-headers browser-cache cache-control

我正在尝试下载(并希望缓存)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进行测试,但都无法发送标题。

6 个答案:

答案 0 :(得分:31)

同样的问题,类似的解决方案

我一直试图确定Google Chrome访问我正在开发的网站时为何不会发送If-None-Match标题。 (Chrome 46.0.2490.71米,虽然我不确定该版本的相关性。)

这与最终引用的OP(在关于“接受的答案”的评论中)有所不同 - 尽管非常相似 - 但它解决了同样的问题:

浏览器不会在后续请求中发送If-None-Match标头"它应该" (即,通过PHP或类似的服务器端逻辑已用于在第一个响应中发送ETagLast-Modified标头。

的先决条件

使用自签名TLS证书可以更改Chrome的缓存行为,该证书会在Chrome中将锁定设置为红色。在尝试解决此类问题之前,请在有效的受信任的根存储中安装自签名证书,并完全重新启动浏览器,如https://stackoverflow.com/a/19102293中所述。

第一顿悟:If-None-Match需要来自服务器的ETag,首先是

我很快意识到,Chrome(可能是大多数或所有其他浏览器)都不会发送If-None-Match标题,直到 服务器 已发送 ETag标头以响应之前的请求。从逻辑上讲,这是完全合理的;毕竟,当Chrome从未被赋予价值时,Chrome如何发送If-None-Match

这让我看看我的服务器端逻辑 - 特别是当我希望用户代理缓存响应时如何发送标头 - 以确定ETag标头不是什么原因响应Chrome首次请求该资源而发送的内容。我已经计算过将ETag标头包含在我的应用程序逻辑中。

我碰巧使用PHP,所以@ Mehran(OP的)评论跳出来了(他/她说在发送所需的缓存相关之前调用header_remove()标题解决了这个问题。)

坦率地说,我对这个解决方案持怀疑态度,因为a)我很确定PHP默认情况下不会发送任何自己的标头(并且根据我的配置它并不是这样);和b)当我在PHP中设置自定义缓存头之前调用var_dump(headers_list());时,唯一的头集是我在上面有意设置的头集:

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

所以,没有什么可失去的,我尝试在发送自定义标头之前调用header_remove();。令我惊讶的是,PHP开始突然发送ETag标题!

第二次顿悟:gzipping响应改变其哈希值

然后我就像一块砖块一样打我:通过在PHP中指定Content-type标题,我告诉NGINX(我正在使用的网络服务器),一旦PHP将其交回给GZIP响应NGINX!为了清楚起见,我指定的Content-type是在NGINX的gzip类型列表中。

为了彻底,我的NGINX GZIP设置如下,PHP通过php-fpm连接到NGINX:

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可能会删除我在PHP中发送的ETag" gzippable"指定了content-type,并提出了一个现在明显的答案:因为当NGINX gzips时,NGINX会修改PHP传回的响应体!这很有道理;如果ETag与用于生成它的响应不匹配,则无需发送ETag。 NGINX如此聪明地处理这个场景非常光滑。

我不知道NGINX是否一直非常聪明,不会压缩未压缩但包含Posted on Jun 15, 2013 by Massive Bird标题的响应主体,但这似乎是什么'发生在这里。

更新:我找到commentary that explains NGINX's behavior in this regard,反过来引用了有关此主题的两个有价值的讨论:

  1. NGINX forum thread discussing the behavior
  2. Tangentially-related discussion in a project repository;请参阅评论Massive Bird
  3. 为了保留这个有价值的解释,如果它真的消失了,我引用ETag对讨论的贡献:

      

    Nginx在动态gzipping响应时剥离了Etag。这是   根据规范,因为非gzip响应不是逐字节的   与gzipped响应相当。

    然而,NGINX在这方面的行为可能被认为是同一规范中的一点点缺陷

      

    ...还说有一种称为弱Etags的东西(一种Etag值   以W /)为前缀,并告诉我们它可用于检查是否有响应   在语义上是等价的。在这种情况下,Nginx不应该搞乱   它。不幸的是,这个检查从未进入源代码树[引用现在充满垃圾邮件,遗憾的是]。"

    我不确定NGINX目前在这方面的态度,特别是,它是否增加了对弱势"的支持。 ETag的。

    那么,解决方案是什么?

    那么,将ETag重新纳入响应的解决方案是什么?在PHP中执行gzipping,以便NGINX看到响应已经被压缩,并且只是将其传递,同时保持ob_start('ob_gzhandler'); 标头完整:

    ETag

    在发送标头和响应正文之前我添加了此调用后,PHP开始向每个响应发送Ctrl+F5值。是的!

    其他经验教训

    以下是从我的研究中收集到的一些有趣的花絮。在尝试测试服务器端缓存实现时,无论是使用PHP还是其他语言,这些信息都非常方便。

    Chrome及其开发者工具" Net"面板,行为方式不同 取决于请求的启动方式

    如果请求是新鲜的",例如按Cache-Control: no-cache Pragma: no-cache ,则Chrome会发送以下标题:

    200 OK

    ,服务器响应F5

    如果请求仅使用Pragma: no-cache ,则Chrome会发送以下标题:

    304 Not Modified

    ,服务器响应Cache-Control: no-cache Pragma: no-cache

    最后,如果通过点击您已查看的网页的链接,将焦点放入Chrome的地址栏并按Enter键,Chrome会发送请求这些标题:

    200 OK (from cache)

    ,服务器响应Cache-Control: no-cache

    虽然这种行为一开始有点令人困惑,但如果你不知道它是如何工作的,那么它是理想的行为,因为它允许我们非常彻底地测试每个可能的请求/响应场景。

    也许最令人困惑的是,Chrome会自动在传出请求中插入Pragma: no-cache200 OK (from cache)标头实际上 Chrome正在从其缓存中获取响应(如{{1}}回复。)

    这段经历对我而言非常有用,我希望其他人在未来找到这种价值分析。

答案 1 :(得分:18)

您的回复标题包括Cache-Control: no-store, no-cache;这些都可以防止缓存。

删除这些值(我认为可以/应该保留must-revalidate, post-check=0, pre-check=0 - 它们告诉浏览器检查服务器是否有更改)。

我会坚持使用Last-Modified(如果仅使用此标准可以检测到对资源的​​更改) - ETag是一个更复杂的事情要处理(特别是如果你想处理它本身就在你的PHP脚本中),Google PageSpeed / YSlow也建议不要这样做。

答案 2 :(得分:1)

将其发布给以后的我...

我遇到了类似的问题,我在响应中发送了ETag,但是HTTP客户端没有在后续请求中发送If-None-Match头(这很奇怪,因为前一天)。

原来我使用http://localhost:9000进行开发(未使用If-None-Match)-通过切换为http://127.0.0.1:9000 Chrome 1 自动开始发送{ {1}}标头再次出现在请求中。

此外-确保未选中If-None-Match

Chrome浏览器:版本71.0.3578.98(正式版本)(64位)

1 我在文档中找不到任何地方-我假设Chrome对此逻辑负责。

答案 3 :(得分:1)

这发生在我身上,原因有两个:

  1. 我的服务器未发送etag响应标头。我通过添加以下内容更新了码头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”标头!

我希望它能对某人有所帮助

答案 4 :(得分:0)

类似问题

我尝试使用If-None-Match标头获取条件GET请求,提供了正确的Etag标头,但在我尝试过的任何浏览器中均无效。

经过大量试用后,我意识到浏览器会将GETPOST视为与同一缓存候选相同的路径。因此,GET EtagCache-Control:"no-cache, private"被正确取消后立即&#34; POST&#34;与X-Requested-With:"XMLHttpRequest"相同的路径,即使它由net_device_ops提供。

希望这对某人有帮助。

答案 5 :(得分:0)

发生这种情况是因为我将缓存大小设置得太小(通过组策略)。

在隐身模式中没有发生,这使我意识到情况可能如此。

已解决问题的解决方案。