吐槽 chrome 的 200 from memory cache 缓存 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
silenceeeee
V2EX    问与答

吐槽 chrome 的 200 from memory cache 缓存

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

    在写一个前端页面,此页面包含一个 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
    argos
        1
    argos  
       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/entry58b82f602f301e006c545b05

    通常刷新 (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     Solana     3789 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 36ms UTC 05:08 PVG 13:08 LAX 21:08 JFK 00:08
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86