49

在阅读了这里的许多文章和一些问题后,我终于成功激活了 Apache mod_expires,告诉浏览器它必须缓存图像 1 年

<filesMatch "\.(ico|gif|jpg|png)$">
  ExpiresActive On
  ExpiresDefault "access plus 1 year"
  Header append Cache-Control "public"
</filesMatch>

幸运的是,服务器响应似乎是正确的:

HTTP/1.1 200 OK 
Date: Fri, 06 Apr 2012 19:25:30 GMT 
Server: Apache 
Last-Modified: Tue, 26 Jul 2011 18:50:14 GMT 
Accept-Ranges: bytes 
Content-Length: 24884 
Cache-Control: max-age=31536000, public 
Expires: Sat, 06 Apr 2013 19:25:30 GMT
Connection: close
Content-Type: image/jpeg 

好吧,我认为这会阻止浏览器下载,甚至会在 1 年内向服务器查询图像。但这部分是正确的:因为如果您关闭并重新打开浏览器,浏览器将不再从服务器下载图像,但浏览器仍会向服务器查询每个图像的 HTTP 请求

如何强制浏览器停止对每个图像发出 HTTP 请求?即使这些 HTTP 请求后面没有下载图像,它们仍然是向服务器发出的请求,这会不必要地增加延迟并减慢页面呈现速度!

我已经告诉浏览器它必须将图像缓存 1 年!为什么浏览器仍然会向服务器查询每个图像(即使它没有下载图像)?!


查看 FireBug 中的网络图(菜单 FireBug > Net > Images)我可以看到不同的缓存行为(我显然从浏览器缓存完全为空开始,我使用“清除所有历史记录”强制在浏览器上删除缓存):

  • 当页面第一次加载时,所有图像都被下载(如果我通过单击浏览器的重新加载页面按钮强制重新加载页面,也会发生同样的事情)。这是有道理的!

  • 当我浏览网站并返回同一页面时,根本没有下载图像,浏览器甚至不会向服务器查询任何图像。这是有道理的,(我想在浏览器关闭时也看到这种行为)!

  • 当我关闭浏览器并在同一页面上再次打开它时,愚蠢的浏览器无论如何都会对每个图像向服务器发出一次 HTTP 请求:它不会下载图像,但它仍然发出 HTTP 请求,就像浏览器查询关于图像的服务器(服务器回复 200 OK)。这是让我恼火的一个!

如果您有兴趣,我还附上下面的图表:

在此处输入图像描述

在此处输入图像描述

编辑:刚刚也使用 FireFox 11.0 进行了测试,以确保这不是我的 FireFox 3.6 太旧的问题。同样的事情发生!!!我还测试了 Google 站点和 Stackoverflow 站点,它们都发送了,Cache-Control: max-age=...但是一旦浏览器关闭并在同一页面上再次打开,浏览器仍然会为每个图像向服务器发出 HTTP 请求,服务器响应后浏览器不会下载图像(正如我上面解释的那样),但它仍然发出增加查看页面时间的该死请求。

EDIT2:并按照此处Last-Modified的建议删除标题,并不能解决问题,也没有任何区别。

4

10 回答 10

27

您看到的行为是预期的(有关更多详细信息,请参阅RFC7234),指定的行为:

无论缓存状态如何,所有现代浏览器都会针对显示的每个页面元素向服务器发送 HTTP 请求。这是应 Web 服务(尤其是广告网络)的要求做出的设计决策,以确保 HTTP 服务器能够维护每个元素的每个显示的记录。

如果浏览器没有发出这些请求,服务器将永远不会被通知图像已显示给用户。对于广告网络来说,这将是灾难性的。早期,广告网络通过使用随机生成的名称(例如:'coke_ad_1_98719283719283.gif')提供相同的广告图像来绕过这个问题。然而,对于 ISP 而言,这种做法导致数据传输量大幅增加,因为他们的每个用户都在重新下载这些相同的广告图像,绕过他们 ISP 运行的任何缓存/代理服务器。

所以达成了休战:浏览器总是会发送 HTTP 请求,即使是未过期的缓存元素。服务器将响应 HTTP 304 状态代码(“未修改”)。这允许服务器记录图像显示给客户端的事实。结果,广告网络通常停止使用随机图像名称来绕过网络缓存服务器。

这给了广告网络他们想要的东西——显示的每张图像的记录——它给了 ISP 他们想要的东西——可缓存的图像和静态内容。

这就是为什么您无法阻止浏览器发送对缓存页面元素的 HTTP 请求的原因。

但是,如果您查看与 html5 一起提供的其他可用客户端解决方案,则有一个范围可以防止资源加载

  1. 缓存清单(尽管有问题)
  2. IndexedDB(不错的异步功能,允许 blob 存储)
  3. 本地存储(非异步)
于 2012-04-16T15:21:40.243 回答
17

您使用了错误的工具来分析请求。

我推荐真正有用的 Firefox 插件Live HTTP 标头,这样您就可以看到网络上真正发生的事情。

并且可以肯定的是,您可以 ssh/putty 您的服务器并执行类似的操作

tail -f /var/log/apache2/access.log
于 2012-05-16T14:52:43.237 回答
12

“重新加载”和“刷新”是有区别的。只是导航到带有后退和前进按钮的页面通常不会启动新的 HTTP 请求,但特别是按 F5 来“刷新”页面会导致浏览器再次检查其缓存。这取决于浏览器,但似乎是 FF 和 Chrome 的规范(即能够轻松查看其网络流量的浏览器。)按 F6,输入应聚焦 URL 地址栏,然后“转到”它,这应该重新加载页面,但不要仔细检查页面上的资产。

更新:澄清前后导航行为。它在浏览器中称为“Back Forward Cache”或BFCache。当您使用后退/前进按钮导航时,目的是向您显示与您在自己的时间轴中看到页面时完全相同的页面。使用 back 和 forward 时不会发出服务器请求,即使服务器缓存标头表明特定项目已过期。

如果您在开发人员网络面板中看到 (200 OK BFCache),则服务器从未被击中 - 甚至询问 if-modified-since。

http://www.softwareishard.com/blog/firebug/firebug-tip-what-the-heck-is-bfcache/

于 2013-02-02T17:43:47.067 回答
7

如果我使用 F5 或 F5 + Ctrl 强制刷新,则会发送请求。但是,如果我关闭浏览器并再次输入 url,则不会发送任何请求。我测试是否发送请求的方法是在服务器上的开始请求上使用断点,即使没有发送请求,它仍然在 Firebug 中显示为已完成 7 毫秒的等待,因此请注意这一点。

于 2012-04-12T15:32:19.470 回答
6

你在这里描述的并不反映我的经验。如果内容是使用 no-store 指令提供的,或者您进行了显式刷新,那么是的,我希望它返回到原始服务器,否则它应该在浏览器重新启动时被缓存(假设它被允许并且可以写入缓存文件)。

更详细地查看您的瀑布(这很棘手,因为它们有点小且模糊),浏览器似乎正在做它应该做的事情 - 它图像条目 - 但这些只是从本地缓存加载而不是从源服务器 - 检查响应中的“日期”标头(为什么您认为它需要毫秒而不是秒?)。这就是为什么它们的颜色不同。

于 2012-04-07T22:01:41.010 回答
4

在自己花费大量时间寻找合理的答案之后,我发现下面的链接最有用,它确实回答了这里提出的问题。

https://webmasters.stackexchange.com/questions/25342/headers-to-prevent-304-if-modified-since-head-requests

于 2014-02-04T00:16:06.437 回答
0

您在 Chrome 中看到的不是实际 HTTP 请求的记录,而是资产请求的记录。Chrome 这样做是为了向您显示页面实际上正在请求资产。但是,此视图实际上并不能真正指示请求是否正在发出。如果资产被缓存,Chrome 将永远不会真正创建底层 HTTP 请求。

您还可以通过将鼠标悬停在时间线中的紫色段上来确认这一点。缓存资源将(from cache)在工具提示中有一个。

为了查看实际的 HTTP 请求,您需要查看较低的级别。在某些浏览器中,这可以通过插件(如 Live HTTP Headers)来完成。

但实际上,要验证请求实际上并未发出,您需要检查服务器日志或使用调试代理,如 Charles 或 Fiddler。这将在 HTTP 级别上起作用,以确保请求实际上没有发生。

于 2014-12-18T16:15:54.257 回答
0

如果这是生死攸关的问题(如果您想以这种方式优化页面加载,或者无论如何都想尽可能减少服务器上的负载),那么有一种解决方法。

首次请求图像后,使用 HTML5本地存储缓存图像。

  • [+]您可以阻止浏览器发送 HTTP 请求,无论用户如何努力(F5、ctrl+F5、简单地重新访问页面等),99% 的 HTTP 请求都会返回 304(未修改)

  • [-]您必须为此付出一些额外的努力来支持 javascript。

  • [-]图像存储在 base64 中(我们不能存储二进制数据),这就是为什么它们每次都在客户端被解码。这通常很快而且没什么大不了的,但它仍然是客户端的一些额外 cpu 使用量,应该牢记在心。

  • [-]本地存储是有限的。您可以针对每个域使用约 5mb 的数据(注意:base64 将图像的原始大小增加了约 30%)。

  • [?]大多数浏览器都支持。http://caniuse.com/#search=localstorage

例子

测试

于 2014-12-01T05:05:01.930 回答
0

缓存验证和 304 响应

在多种情况下 Internet Explorer 需要检查缓存条目是否有效:

  • 缓存的条目没有过期日期,并且是在浏览器会话中首次访问内容

  • 缓存的条目有一个过期日期,但它已经过期

  • 用户已通过单击刷新按钮或按 F5 请求页面更新

如果缓存条目有最后修改日期,IE 在 GET 请求消息的 If-Modified-Since 标头中发送它:

GET /images/logo.gif HTTP/1.1
Accept: */*
Referer: http://www.google.com/
Accept-Encoding: gzip, deflate
If-Modified-Since: Thu, 23 Sep 2004 17:42:04 GMT
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;)
Host: www.google.com

服务器检查 If-Modified-Since 标头并做出相应响应。如果内容自指定的日期/时间以来没有更改,它会回复状态代码 304 和仅包含标题的响应消息:

HTTP/1.1 304 Not Modified
Content-Type: text/html
Server: GWS/2.1
Content-Length: 0
Date: Thu, 04 Oct 2004 12:00:00 GMT

响应可以快速下载,因为它不包含任何内容,并导致 IE 从缓存中读取所需的数据。实际上,它就像重定向到本地浏览器缓存。

如果请求的对象自 If-Modified-Since 标头中的日期/时间以来实际上已更改,则服务器以状态代码 200 响应并提供资源的修改版本。

于 2015-02-22T17:13:10.757 回答
0

这个问题在网站管理员堆栈交换网站上有更好的答案。

更多信息,也引用在上面的链接中,在httpwatch

根据这篇文章:

在多种情况下 Internet Explorer 需要检查缓存条目是否有效:

  • 缓存的条目没有过期日期,并且是在浏览器会话中首次访问内容
  • 缓存的条目有一个过期日期,但它已经过期
  • 用户已通过单击刷新按钮或按 F5 请求页面更新

    在此处输入代码

于 2015-11-12T23:28:52.333 回答