吐槽 chrome 的 200 from memory cache 缓存

2017-04-21 10:25:10 +08:00
 silenceeeee

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

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

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

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

10822 次点击
所在节点    问与答
18 条回复
tianshuang
2017-04-21 10:37:28 +08:00
检查下 Response Headers 除了 ETag 是否还含有 Cache-Control 等 header ,是否是其他 header 共同决定缓存策略导致的。
silenceeeee
2017-04-21 10:40:50 +08:00
@tianshuang 没有的,我检查过了。
njstt
2017-04-21 10:52:08 +08:00
你是不是设置了 cache-control 如果有 cache-control 且在有效期内是不会请求的
silenceeeee
2017-04-21 10:58:09 +08:00
@njstt 没有的,我强调了: safari 会进行正常的 Etag 验证。如果存在诸如 max-age 这样的 header ,那 safari 也不会进行过期验证了。
morethansean
2017-04-21 11:11:48 +08:00
morethansean
2017-04-21 11:12:40 +08:00
楼上打快了发送了……
上面链接有讲到……每个浏览器不一样, firefox 是最后修改到现在的 10%,不知道 chrome 的……
otakustay
2017-04-21 11:56:12 +08:00
你不设 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
2017-04-21 12:09:28 +08:00
有 Disable cache 选项
otakustay
2017-04-21 12:15:42 +08:00
另外 200 from memory cache 根本不是什么 Chrome 自己实现的策略,只要在本地 Cache 中 Chrome 的 devtool 就会显示这个信息,以区别是从远程获取的 200 响应

我觉得楼主还是认真把 HTTP 协议和 Browser Cache 搞清楚再说,这样胡乱毫无根据的猜测并不是一个工程师应该有的样子
yatessss
2017-04-21 12:28:33 +08:00
缓存,首先会走强缓存吧,如果强缓存失效,才会走协议缓存,你先把强缓存设置成不缓存
silenceeeee
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
2017-04-21 17:45:44 +08:00
@randal 这个就比较尴尬了,一时间把这个给望了。-_-
otakustay
2017-04-21 19:57:19 +08:00
@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
2017-04-21 21:55:40 +08:00
@otakustay 恩 明白了!谢谢
gbin
2019-03-14 18:49:41 +08:00
@otakustay 今天遇到了类似问题,开始很怀疑 Chrome 没有遵守 HTTP 协议,读了《 HTTP 权威指南》缓存那章两三遍都没搞懂为什么 from memery cache,最好结合你的回答搞懂了,不得不佩服 V 站大神无处不在呀。
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
2019-07-27 11:22:29 +08:00
隔了两年看到这个帖子


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

页面里的静态资源直接从本地缓存获取,不会再有 304 的过程
yhxx
2019-07-28 11:46:23 +08:00
@yhxx 更正一下,页面里的静态资源会遵循资源本身的缓存设置,不会在刷新页面( F5 )时被添加"max-age=0"请求头

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/356353

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX