V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
silenceeeee
V2EX  ›  问与答

吐槽 chrome 的 200 from memory cache 缓存

  •  
  •   silenceeeee · 2017-04-21 10:25:10 +08:00 · 10817 次点击
    这是一个创建于 2773 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在写一个前端页面,此页面包含一个 img 标签。假设该标签引用的图片地址是 .../01.jpg 。

    服务器程序是 Nginx ,设置了 Etag 。

    当 01.jpg 更改后, chrome 不会从服务器更新它,而是会返回 200 from memory cache ( chrome 自己实现的缓存策略) safari 则会进行正常的 HTTP 请求(验证 Etag ,如果资源未更改,则返回 304 )

    不太能理解的是, chrome 的 dev tools 是提供给开发者使用的,这样相当于更改了正常的 HTTP 处理逻辑。感觉对开发者来讲,容易造成困惑。另外,这种情况,大家一般是如何处理的呢?只能清理 chrome 缓存吗?

    18 条回复    2019-07-28 11:46:23 +08:00
    tianshuang
        1
    tianshuang  
       2017-04-21 10:37:28 +08:00
    检查下 Response Headers 除了 ETag 是否还含有 Cache-Control 等 header ,是否是其他 header 共同决定缓存策略导致的。
    silenceeeee
        2
    silenceeeee  
    OP
       2017-04-21 10:40:50 +08:00
    @tianshuang 没有的,我检查过了。
    njstt
        3
    njstt  
       2017-04-21 10:52:08 +08:00
    你是不是设置了 cache-control 如果有 cache-control 且在有效期内是不会请求的
    silenceeeee
        4
    silenceeeee  
    OP
       2017-04-21 10:58:09 +08:00
    @njstt 没有的,我强调了: safari 会进行正常的 Etag 验证。如果存在诸如 max-age 这样的 header ,那 safari 也不会进行过期验证了。
    morethansean
        6
    morethansean  
       2017-04-21 11:12:40 +08:00
    楼上打快了发送了……
    上面链接有讲到……每个浏览器不一样, firefox 是最后修改到现在的 10%,不知道 chrome 的……
    otakustay
        7
    otakustay  
       2017-04-21 11:56:12 +08:00   ❤️ 3
    你不设 Cache-Control 的话就会被缓存住这很正常啊,因为 Cache-Control 是 Private 啊

    Unless specifically constrained by a cache-control
    directive, a caching system MAY always store a successful response

    怎么设这个头就看这吧:
    http://stackoverflow.com/questions/49547/how-to-control-web-page-caching-across-all-browsers
    randal
        8
    randal  
       2017-04-21 12:09:28 +08:00
    有 Disable cache 选项
    otakustay
        9
    otakustay  
       2017-04-21 12:15:42 +08:00
    另外 200 from memory cache 根本不是什么 Chrome 自己实现的策略,只要在本地 Cache 中 Chrome 的 devtool 就会显示这个信息,以区别是从远程获取的 200 响应

    我觉得楼主还是认真把 HTTP 协议和 Browser Cache 搞清楚再说,这样胡乱毫无根据的猜测并不是一个工程师应该有的样子
    yatessss
        10
    yatessss  
       2017-04-21 12:28:33 +08:00
    缓存,首先会走强缓存吧,如果强缓存失效,才会走协议缓存,你先把强缓存设置成不缓存
    silenceeeee
        11
    silenceeeee  
    OP
       2017-04-21 17:44:32 +08:00
    @otakustay 首先,谢谢你的回答。

    前提条件: Nginx 默认开启了 Etag ( HTTP 请求也返回了 Etag )

    我观察到的现象是:


    1. 当我刷新 HTML 页面的时候, chrome Dev Tools 显示 1.jpg 的状态为 200 from memory cache;

    2. 当我在 safari 和 firefox 里面刷新 HTML 的时候, Dev Tools 都是返回 304 ,而不是 200 ,请求头都包含了 If-None-Match

    然后我理解的是:没有任何 Cache-Control ,但是设置了 Etag ,每次请求都应该带上 If-None-Match 来验证 Etag 。而且 firefox 、 safari 都如我预期所想,在请求的时候加上了 If-None-Match 。

    问题就出现在这里吧,我就直接在心里定义了一个结果:没有设置类似 max-age 这样的缓存时间,则浏览器不应该缓存结果。

    所以整个解释如下:

    在服务器没有显式返回 Cache-Control 相关 header 时,浏览器可以决定是否缓存资源。而这里 chrome 选择了缓存文件,但是 safari 、 firefox 并没有缓存。

    不知道这样理解对不对,望指点一二。
    silenceeeee
        12
    silenceeeee  
    OP
       2017-04-21 17:45:44 +08:00
    @randal 这个就比较尴尬了,一时间把这个给望了。-_-
    otakustay
        13
    otakustay  
       2017-04-21 19:57:19 +08:00   ❤️ 5
    @silenceeeee 你这么说就对了,事实上就是你对 HTTP 协议存在误解而自己下了个预定义,事实上浏览器并没有做错

    浏览器的缓存有 2 种
    一种叫验证性缓存,用 ETag 、 Last-Modified 、 If-None-Match 、 If-Modified-Since 来控制,其特点是会发一个请求给服务器来确认缓存是否有效,如果有效就返回 304 ,省去传输内容的时间
    另一种叫非验证性缓存,或者有些人称为强缓存,用 Cache-Control 、 Expires 、 Pragma 来控制,其特点是一但有效就在有效期内不会发任何请求到服务器

    从描述也能很容易看出来,非验证性缓存的优先级是高于验证性缓存的,因为有它在就根本不会发请求,自然也没有什么 If-None-Match 之类的东西出现的机会了
    你看到的 200 from memory cache 就是非验证性缓存

    那么为什么在 Chrome 下会有非验证性缓存呢?就是因为你没有设置 Cache-Control 这个头,没有这个头的话,其默认值是 Private ,在标准中也明确说了:

    Unless specifically constrained by a cache-control
    directive, a caching system MAY always store a successful response

    翻译一下:如果没有 Cache-Control 进行限制,缓存系统**可以**对一个成功的响应进行存储

    很显然, Chrome 是遵守标准的,它在没有检查到 Cache-Control 的时候对响应做了非验证性缓存,所以你看到了 200 from memory cache
    同时 Safari 也是遵守标准的,因为标准只说了**可以**进行存储,而非**应当**或者**必须**,所以 Safari 不进行缓存也是合理的

    我们可以理解为,没有 Cache-Control 的情况下,缓存不缓存就看浏览器高兴,你也没什么好说的。那么你如今的需求是“明确不要非验证性缓存”,则从标准的角度来说,你**必须**指定相应的 Cache-Control 头

    所以综合下来,错的在你
    silenceeeee
        14
    silenceeeee  
    OP
       2017-04-21 21:55:40 +08:00 via iPhone
    @otakustay 恩 明白了!谢谢
    gbin
        15
    gbin  
       2019-03-14 18:49:41 +08:00 via Android
    @otakustay 今天遇到了类似问题,开始很怀疑 Chrome 没有遵守 HTTP 协议,读了《 HTTP 权威指南》缓存那章两三遍都没搞懂为什么 from memery cache,最好结合你的回答搞懂了,不得不佩服 V 站大神无处不在呀。
    Fnlxuer
        16
    Fnlxuer  
       2019-06-15 15:57:04 +08:00
    @silenceeeee 我认为你的理解有误,01.jpg 均被 Chrome、Safari 和 Firefox 缓存,造成响应的不同在于浏览器对刷新操作的不同实现。下面详细说说我的看法。

    1. 响应被缓存储存的条件
    (引用标准 RFC7234,原文见 https://tools.ietf.org/html/rfc7234#section-3

    HTTP 缓存要求的重点是防止缓存存储不可复用的响应,或者不恰当地复用已存储的响应,而不是批准缓存总是存储和复用特定的响应。

    存储条件:
    - 请求方法可被缓存且被缓存理解;
    - 响应状态码被缓存理解;
    - no-store 缓存指令不在请求或响应中;
    - 如果是共享缓存:private 指令不在响应中, Authorization 字段不在请求中 (除非响应明确允许,如使用 public 指令)
    - 符合以下情况之一:Expires、max-age、共享缓存和 s-maxage、可被缓存的状态码、public、允许缓存的 Cache Control 扩展

    按条件分析:请求方法 GET、200 响应(可被缓存),没有其它指令,所以根据标准,该响应可被储存。
    实际上,从刷新后的 200 from memory cache、304 这两状态码来看,01.jpg 已经被缓存,只不过 Chrome 是命中了已储存的 200 响应,而 Safari 和 Firefox 是经条件请求对已储存的 200 响应验证成功后返回了 304 响应。

    2. Cache-Control 没有默认值
    https://stackoverflow.com/questions/14496694/whats-default-value-of-cache-control

    如果服务器端没有设置 Cache-Control 字段,那么响应里自然没有。

    3. 关于 from memory cache 和 304 响应( RFC7234 )

    响应被缓存储存后,优先复用该响应来满足未来的等效请求。
    RFC 中的复用条件:
    - effective request URI 和 selecting header 均匹配
    - 与已存储的响应相关联的请求方法允许该响应用于出现的请求;
    - 请求和响应中均没有 no-cache,除非成功验证已存储的响应
    - 已存储的响应是以下情况之一:新鲜的、被成功验证的、允许作为陈旧响应来提供
    如果上述条件均满足,而且是新鲜的响应,那将复用缓存里的相应响应,这将导致 from memory cache (状态码为相应响应的)。
    如果响应过期,那么将发送带 ETag 中验证器的条件请求到服务器,验证响应有没有发生变化,如果没有变化,将返回 304 (Not Modified) 响应。

    4. 响应的过期判断( RFC7234 )

    源服务器一般会为响应分配未来的显式过期时间, 通过 s-maxage、max-age、Expires 这几种方式,并根据一定的算法计算当前响应的 age (大概为 now - Date ),如果 age > max-age,则该已储存的响应已经过期,通常需要验证才能复用。

    具体到你的例子,由于你没有提供显式过期时间,浏览器将使用启发式的算法计算过期时间(通常算法为 (date_value - last_modified_value) / 10 ),上面 @morethansean 也提到了。
    现代浏览器的算法应该都差不多,所以应该不是 Chrome 计算认为已存储的响应没有过期,而 Safari 和 Firefox 计算认为已经过期,必须验证。

    5. 关于浏览器对刷新操作的实现
    https://juejin.im/entry/58b82f602f301e006c545b05

    通常刷新 (F5) 会强制验证已储存的响应,如果验证成功,返回 304 响应,如例子 Safari 和 Firefox 的行为。
    而 Chrome 改变了刷新按钮的重新验证机制,仅验证主要资源,而子资源直接从缓存中读取。这就是对 01.jpg 的请求返回 200 from memory cache 的原因。
    yhxx
        17
    yhxx  
       2019-07-27 11:22:29 +08:00
    隔了两年看到这个帖子


    从 chrome 54 开始,chrome 把页面刷新之后的操作改成了只验证页面本身

    页面里的静态资源直接从本地缓存获取,不会再有 304 的过程
    yhxx
        18
    yhxx  
       2019-07-28 11:46:23 +08:00
    @yhxx 更正一下,页面里的静态资源会遵循资源本身的缓存设置,不会在刷新页面( F5 )时被添加"max-age=0"请求头
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1071 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 19:11 · PVG 03:11 · LAX 11:11 · JFK 14:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.