在写一个前端页面,此页面包含一个 img 标签。假设该标签引用的图片地址是 .../01.jpg 。
服务器程序是 Nginx ,设置了 Etag 。
当 01.jpg 更改后, chrome 不会从服务器更新它,而是会返回 200 from memory cache ( chrome 自己实现的缓存策略) safari 则会进行正常的 HTTP 请求(验证 Etag ,如果资源未更改,则返回 304 )
不太能理解的是, chrome 的 dev tools 是提供给开发者使用的,这样相当于更改了正常的 HTTP 处理逻辑。感觉对开发者来讲,容易造成困惑。另外,这种情况,大家一般是如何处理的呢?只能清理 chrome 缓存吗?
1
tianshuang 2017-04-21 10:37:28 +08:00
检查下 Response Headers 除了 ETag 是否还含有 Cache-Control 等 header ,是否是其他 header 共同决定缓存策略导致的。
|
2
silenceeeee OP @tianshuang 没有的,我检查过了。
|
3
njstt 2017-04-21 10:52:08 +08:00
你是不是设置了 cache-control 如果有 cache-control 且在有效期内是不会请求的
|
4
silenceeeee OP @njstt 没有的,我强调了: safari 会进行正常的 Etag 验证。如果存在诸如 max-age 这样的 header ,那 safari 也不会进行过期验证了。
|
5
morethansean 2017-04-21 11:11:48 +08:00
|
6
morethansean 2017-04-21 11:12:40 +08:00
楼上打快了发送了……
上面链接有讲到……每个浏览器不一样, firefox 是最后修改到现在的 10%,不知道 chrome 的…… |
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 |
8
randal 2017-04-21 12:09:28 +08:00
有 Disable cache 选项
|
9
otakustay 2017-04-21 12:15:42 +08:00
另外 200 from memory cache 根本不是什么 Chrome 自己实现的策略,只要在本地 Cache 中 Chrome 的 devtool 就会显示这个信息,以区别是从远程获取的 200 响应
我觉得楼主还是认真把 HTTP 协议和 Browser Cache 搞清楚再说,这样胡乱毫无根据的猜测并不是一个工程师应该有的样子 |
10
yatessss 2017-04-21 12:28:33 +08:00
缓存,首先会走强缓存吧,如果强缓存失效,才会走协议缓存,你先把强缓存设置成不缓存
|
11
silenceeeee OP @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 并没有缓存。 不知道这样理解对不对,望指点一二。 |
12
silenceeeee OP @randal 这个就比较尴尬了,一时间把这个给望了。-_-
|
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 头 所以综合下来,错的在你 |
14
silenceeeee OP @otakustay 恩 明白了!谢谢
|
15
gbin 2019-03-14 18:49:41 +08:00 via Android
@otakustay 今天遇到了类似问题,开始很怀疑 Chrome 没有遵守 HTTP 协议,读了《 HTTP 权威指南》缓存那章两三遍都没搞懂为什么 from memery cache,最好结合你的回答搞懂了,不得不佩服 V 站大神无处不在呀。
|
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 的原因。 |
17
yhxx 2019-07-27 11:22:29 +08:00
隔了两年看到这个帖子
从 chrome 54 开始,chrome 把页面刷新之后的操作改成了只验证页面本身 页面里的静态资源直接从本地缓存获取,不会再有 304 的过程 |