【问题标题】:Browser Cache Control, Dynamic Content浏览器缓存控制,动态内容
【发布时间】:2010-12-19 18:14:03
【问题描述】:

问题:我似乎无法让 FireFox 缓存从动态服务器发送的图像

设置:静态 Apache 服务器与后端动态服务器 (mod_perl2) 的反向代理。

这是服务器的请求 URL。它被发送到动态服务器,其中 cookie 用于验证对图像的访问:

请求标头

Host:  <OBSCURED>
User-Agent:  Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.15) Gecko/2009102815 Ubuntu/9.04 (jaunty) Firefox/3.0.15
Accept:  image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset:  ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:  300
Connection:  keep-alive
Referer: <OBSCURED>
Cookie:  pz_cred=4KCNr0RM15%2FJCOt%2BEa6%2BL62z%2Fxvbp2xNQHY5pJw5d6Q
Pragma:  no-cache
Cache-Control: no-cache

动态服务器将图像流回服务器,并提供以下响应:

响应标头

Date:  Tue, 24 Nov 2009 04:28:07 GMT
Server:  Apache/2.2.11 (Ubuntu) mod_apreq2-20051231/2.6.0 mod_perl/2.0.4 Perl/v5.10.0
Cache-Control: public, max-age=31536000
Content-Length:  25496
Content-Type:  image/jpeg
Via: 1.1 127.0.1.1:8081
Keep-Alive:  timeout=15, max=75
Connection:  Keep-Alive

到目前为止,一切都很好(我认为)。但是,重新加载页面时,图像不会出现缓存,并再次发送请求:

请求标头

Host: <OBSCURED>
User-Agent:  Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.15) Gecko/2009102815 Ubuntu/9.04 (jaunty) Firefox/3.0.15
Accept:  image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset:  ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:  300
Connection:  keep-alive
Referer: <OBSCURED>
Cookie:  pz_cred=4KCNr0RM15%2FJCOt%2BEa6%2BL62z%2Fxvbp2xNQHY5pJw5d6Q
Cache-Control: max-age=0

似乎不应该发生请求,因为浏览器应该已经缓存了图像。实际上,收到了 200 响应,与第一个相同,并且图像似乎被重新获取(尽管浏览器似乎确实在使用缓存的图像)。

上面的重新加载请求标头中的 Cache-Control: max-age=0 似乎暗示了该问题。

有人知道为什么会这样吗?也许是响应中的 Via 标头导致了问题?

【问题讨论】:

    标签: browser mod-perl2 cache-control


    【解决方案1】:

    原来的请求有

    Cache-Control: no-cache
    

    它告诉所有中间 HTTP 缓存(包括 Firefox 的)您不想使用缓存响应,而是希望从源 Web 服务器本身获取响应。

    回复说:

    Cache-Control: public, max-age=31536000
    

    告诉大家,就源服务器而言,响应可能会被缓存。服务器似乎配置为启用缓存 PNG 图像:HTTP 1.1(第 14.21 节)说:

    注意:如果响应包含 具有 max-age 的 Cache-Control 字段 指令(见第 14.9.3 节),即 指令覆盖 Expires 字段。

    你的第二个请求说:

    Cache-Control: max-age=0
    

    它告诉所有中间 HTTP 缓存你不会接受任何超过 0 秒的缓存响应。

    需要注意的一点是:如果您在 Firefox 中点击“重新加载”按钮,您将要求从源 Web 服务器重新加载。要测试图像的缓存,请离开页面并返回,或在新选项卡中打开它。不知道为什么你第一次看到 no-cache 而第二次看到 max-age=0。

    顺便说一句,我喜欢 Firefox 的 FireBug 插件。您可以查看请求和响应标头以及其他各种好东西。

    【讨论】:

    • 嗨吉姆:感谢您的帮助!当我按下 shift-reload 时,第一个请求来自 Firefox。这似乎是合适的,因为我明确要求“新鲜”的内容。第一个反应似乎也是正确的;它具有预期的标题。第二个请求发生在浏览器重新加载 /without/ shift 时。期望是firefox缓存然后会尊重第一个响应,例如最大年龄=31536000。看起来服务器说所有图像(图像/ *)都是可缓存的。如果这一切都是正确的,那么问题是是什么让 FireFox 忽略了缓存指令?
    • 我应该补充一下,在 FireFox 中启用了缓存,并且它成功地缓存了来自静态服务器的图像。我也删除了 via 标题,但没有爱。
    【解决方案2】:

    我之前的回答只是部分正确。

    问题是 FireFox 3 处理重新加载事件的方式。显然,它几乎总是从源服务器再次请求内容。因此Cache-Control: max-age=0 请求标头。

    Firefox确实在重新加载时使用缓存的图像来呈现页面,但它仍然会在“后台”发出所有更新它们的请求。然后在它们进来时替换它们。

    因此,页面呈现速度快,YSlow 报告缓存内容。但是服务器仍然被钉死。

    解决方法是询问动态服务器脚本中的传入标头,并确定是否提供了“If-Modified-Since”标头。如果是这种情况,并且确定内容没有更改,则返回 HTTP_NOT_MODIFIED (304) 响应。

    这不是最佳选择——我宁愿 Firefox 根本不发出请求——但它会将页面加载时间缩短一半,并大大减少带宽。鉴于 Firefox 在重新加载时的工作方式,这似乎是最好的解决方案。

    其他评论:Jim Ferran 关于导航离开页面并返回的观点有其优点——始终使用缓存,并且没有请求发出(+1 给 Jim)。此外,动态添加的内容(例如,初始加载后的 AJAX 调用)似乎也使用缓存。

    希望这对我以外的人有所帮助:)

    【讨论】:

      【解决方案3】:

      看起来解决了:

      • 通过标头删除了代理
      • 添加了 Last-Modified 标头
      • 添加了一个遥远的未来到期日期

      Firebug 仍然显示来自原始服务器的 200 个响应,但是,YSlow 将图像识别为缓存。根据 YSlow,新鲜时的总图像下载大小大于 500K;缓存启动后,它显示 0K 下载大小。

      这是来自 Origin 服务器的响应标头:

      Date: Tue, 24 Nov 2009 08:54:24 GMT
      Server: Apache/2.2.11 (Ubuntu) mod_apreq2-20051231/2.6.0 mod_perl/2.0.4 Perl/v5.10.0
      Last-Modified: Sun, 22 Nov 2009 07:28:25 GMT
      Expires: Tue, 30 Nov 2010 19:00:25 GMT
      Content-Length: 10883
      Content-Type: image/jpeg
      Keep-Alive: timeout=15, max=89
      Connection: Keep-Alive
      

      由于我请求图像的方式,这些日期是否是静态的并不重要;我的应用在请求图像之前知道最后一次修改时间,并将其附加到客户端的请求 URL 以为每个图像版本创建一个唯一的 URL,例如http://myserver.com/img/125.jpg?20091122(信息来自 AJAX JSON 提要)。例如,我可以将最后修改日期设为 2000 年 1 月 1 日,并将到期日期设为 2050 年的某个时间。

      如果 YSlow 是正确的 - 并且性能测试表明它是正确的 - 那么 FireBug 应该真正报告这些本地缓存命中而不是 200 响应。

      【讨论】:

      • ps 我已经向 FireBug 提交了一个错误,报告来自本地缓存的 200 个响应