同样的问题,类似的解决方案
If-None-Match
我一直在尝试确定为什么 Google Chrome在访问我正在开发的网站时不会发送标题。(Chrome 46.0.2490.71 m,虽然我不确定版本的相关性。)
这是一个与最终引用的 OP 不同(尽管非常相似)的答案(在关于已接受答案的评论中),但它解决了相同的问题:
浏览器不会If-None-Match
在“应该”的后续请求中发送标头(即,服务器端逻辑,通过 PHP 或类似方法,已用于在第一个响应中发送ETag
或标头)。Last-Modified
先决条件
使用自签名 TLS 证书(将 Chrome 中的锁变为红色)会更改 Chrome 的缓存行为。在尝试解决此类问题之前,请在有效的受信任的根存储中安装自签名证书,并完全重新启动浏览器,如https://stackoverflow.com/a/19102293中所述。
第一个顿悟:If-None-Match 需要来自服务器的 ETag,首先
我很快意识到 Chrome(可能还有大多数或所有其他浏览器)If-None-Match
在服务器 已经发送了一个ETag
标头以响应先前的请求之前不会发送标头。从逻辑上讲,这是完全合理的。毕竟,当 ChromeIf-None-Match
从未被赋予价值时,它怎么能发送呢?
这导致我查看我的服务器端逻辑 - 特别是当我希望用户代理缓存响应时如何发送标头 - 以确定由于什么原因ETag
没有发送标头以响应 Chrome 的第一个请求资源。我已经计算出将ETag
标头包含在我的应用程序逻辑中的努力。
我碰巧在使用 PHP,所以@Mehran(OP)的评论突然出现在我身上(他/她说header_remove()
在发送所需的与缓存相关的标头之前调用可以解决问题)。
坦率地说,我对这个解决方案持怀疑态度,因为 a) 我很确定 PHP 在默认情况下不会发送任何自己的标头(鉴于我的配置,它不会发送);b) 当我var_dump(headers_list());
在 PHP 中设置自定义缓存标头之前调用时,唯一的标头集是我在上面故意设置的:
header('Content-type: application/javascript; charset=utf-8');
所以,没有什么可失去的,我试着header_remove();
在发送我的自定义标题之前打电话。令我惊讶的是,PHPETag
突然开始发送标头!
第二个顿悟:压缩响应会改变它的哈希值
然后我像一袋砖头一样打我:通过Content-type
在 PHP 中指定标头,我告诉 NGINX(我正在使用的网络服务器)一旦 PHP 将响应交还给 NGINX,就将其 GZIP 压缩!需要明确的是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 可能会ETag
在指定“gzippable”内容类型时删除我在 PHP 中发送的内容,并想出了一个现在显而易见的答案:因为 NGINX 修改了当 NGINX 压缩它时 PHP 传回的响应正文!这很有意义;ETag
当它与用于生成它的响应不匹配时,发送它是没有意义的。NGINX 如此智能地处理这种情况是非常巧妙的。
我不知道 NGINX 是否一直足够聪明,不会压缩未压缩但包含ETag
headers的响应主体,但这似乎就是这里发生的事情。
更新:我找到了解释 NGINX 在这方面的行为的评论,这反过来又引用了关于这个主题的两个有价值的讨论:
- NGINX 论坛线程讨论该行为。
- 项目存储库中相关的讨论;见评论
Posted on Jun 15, 2013 by Massive Bird
。
为了保留这个有价值的解释,如果它碰巧消失了,我引用Massive Bird
对讨论的贡献:
Nginx 在动态压缩响应时会剥离 Etag。这是根据规范,因为非压缩响应与压缩响应不是逐字节可比的。
然而,NGINX 在这方面的行为可能会被认为在同一规范中存在轻微缺陷
... 还说有一种叫做弱 Etags 的东西(一个以 W/ 为前缀的 Etag 值),并告诉我们它可以用来检查响应是否在语义上等价。在这种情况下,Nginx 不应该乱用它。不幸的是,该检查从未进入源代码树[可悲的是,引文现在充满了垃圾邮件]。”
我不确定 NGINX 目前在这方面的态度,特别是它是否增加了对“弱”Etags 的支持。
那么,解决方案是什么?
那么,ETag
恢复响应的解决方案是什么?在 PHP 中执行 gzipping,以便 NGINX 看到响应已经被压缩,并简单地传递它,同时保持ETag
标头完整:
ob_start('ob_gzhandler');
一旦我在发送标头和响应正文之前添加了这个调用,PHP 就开始在ETag
每个响应中发送值。是的!
其他经验教训
以下是从我的研究中收集到的一些有趣的花絮。在尝试测试服务器端缓存实现(无论是 PHP 还是其他语言)时,此信息非常方便。
Chrome 及其开发者工具“网络”面板的行为取决于请求的发起方式。
如果请求是“新鲜的”,例如,通过按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
和标头,而实际上Chrome 正在从其缓存中获取响应(如响应中所证明的那样)。Pragma: no-cache
200 OK (from cache)
这段经历对我来说相当有用,我希望其他人在未来能找到这种价值分析。