你尚未登录,仅允许查看本站部分内容。请登录使用邀请码注册
vvian00

配置错误产生的差距:200 OK (FROM CACHE) 与 304 NOT MODIFIED 10个回复 专栏 @ 探索

vvian00 发布于 3 年前

本文原载于本人博客:http://www.bokeyy.com/post/200-ok-from-cache-vs-304-not-modified.html

为什么有的缓存是 200 OK (from cache),有的缓存是 304 Not Modified 呢?很简单,看运维是否移除了 Entity Tag。移除了,就总是 200 OK (from cache)。没有移除,就两者会交替出现。

最近在做百度云观测的 nginx 配置优化。从知乎上看到这个问题:“阿里云存储如何让浏览器始终以200 (from cache)缓存图片?”,提问者强调 200 OK (from cache) 和 304 Not Modified 的区别,有感而发。

其实, 200 OK (from cache)  是浏览器没有跟服务器确认,直接用了浏览器缓存;而 304 Not Modified 是浏览器和服务器多确认了一次缓存有效性,再用的缓存。

它们都是在设置了缓存的情况下触发的。

那么,两者触发的时机有什么区别呢?200 OK (from cache) 是直接点击链接访问,输入网址按回车访问也能触发;而 304 Not Modified 是刷新页面时触发,或是设置了长缓存、但 Entity Tags 没有移除时触发。这是经过查阅资料得出的结论。博主实际测试了一下,结论与之相符:

直接访问有缓存的网站都触发 200 OK (from cache)
图1 - 直接访问有缓存的网站都触发 200 OK (from cache)

刷新浏览器会触发 304 Not Modified
图2 - 刷新浏览器则会触发 304 Not Modified

同一域名下,没有 Entity Tag 的资源直接访问,是 200 OK (from cache) 的结果
图3 - 同一域名下,没有 Entity Tag 的资源直接访问,是 200 OK (from cache) 的结果

同一域名下,有 Entity Tag ,直接访问就会触发 304 Not Modified
图4 - 同一域名下,有 Entity Tag ,直接访问就会触发 304 Not Modified

现在一般都会设置长时间的缓存,正确设置方式参考这两篇笔记:

参考文献

后记

搜索了一下,发现这个问题,在网络上并还没有定论,也没发现有人去实测。

想想也对,现在网络那么快,304 Not Modified 还是 200 OK (from cache),如果不是较真地追求速度,可能大家都觉得区别不大,从而也就没发现这个问题了。

最后,本文并不是说影响浏览器缓存只有 ETag 这一个因素的意思,请大家不要误解。只是就“为什么我加了缓存,有的却是 304 Not Modified, 而不是 200 OK(from cache)”这件事给出一个一针见血的原因和解答。

  • 吴双Orange

    不错啊,学习到了

    #1
  • hefangshi

    这个结论不是很准确,实际上这个问题答案全部都在https://tools.ietf.org/html/rfc7234

    简单说一下

    浏览器的缓存机制分为两块,也就是规范中的4.2. Freshness 和 4.3. Validation

    Freshness

    Cache Control与Expires是一组,他们是用来进行Freshness验证,也就是提供客户端检测文件是否足够新鲜,可以无需向服务端发起Validation请求就能保证并未过期可以直接使用。

    所有的from cache的请求实际上都是由于浏览器认为本地的缓存资源足够新鲜,所以无需额外请求而直接使用。

    具体的判断方法可以参考协议,实际上就是根据本地的时间和服务器返回头的约定信息进行对比验证。

    历史问题

    为何有两个参数而不是一个参数则是由于历史原因,在HTTP1.0中定义的是Expires,Expires的值是一个明确的过期时间,而后来使用中发现一旦客户端时间与服务器时间不一致就会引发很多缓存问题。

    因此在HTTP 1.1中新添加了Cache Control,实现了更优的文件过期声明,比如max-age配置,是一个timespan,告知客户端这个文件多长时间不会过期而不是直接告知过期时间。

    Validation

    Last-Modified和ETag则是另一组控制信息,他们用来实现Validation。他们的职责是在本地缓存被浏览器判断可能不够新鲜的时候,会用这两组信息向服务器请求数据,如果服务器内容没有改变,那么约定服务器会返回304 HTTP Code表明这个缓存可以直接使用,无需重新拉取,而一旦服务器内容改变了就会返回200,同时返回新的文件内容。

    历史问题

    至于为何有两组字段来解决这个问题依然是历史问题,在HTTP 1.0中约定的是Last-Modified,他代表的含义是文件最后一次修改的时间,那么这种约定带来的问题是一旦内容是动态生成的,这个时间在服务器端不一定可以正确的生成,其次是Last-Modified只有秒级的精度,如果在一秒内有一次以上的文件修改,这样的缓存就会造成额外的问题。

    因此HTTP1.1中新引入了ETag,他的实现不尽相同,对于动态内容,常规做法是对动态内容做HASH计算,作为ETAG返回,对于静态资源,一般是使用inode+mtime进行计算。

    额外的问题

    ETag也有他自己的问题,所以经常在使用中会关闭ETag。举例来说,同一个文件在不同物理机上的inode是不同的,这就导致了在分布式的Web系统中,当访问落在不同的物理机上时会返回不同的ETag,进而导致304失效,降级为200请求。解决办法也有从ETag算法中剥离inode,只是使用mtime,但是这样实际上和Last-Modified就一样了。当然你也可以额外的做一些改进,使ETag对静态资源的算法也是通过hash计算的。不过由于一般我们会使用CDN技术,因此集群部署中的ETag问题并不会造成太大的影响,所以折腾的人也不太是很多。

    总结

    总结一下,ETag的设置并不会影响Freshness验证,Freshness验证会和浏览器策略以及Cache Control与Expires有关。

    #2
  • vvian00

    ETag 理论很全面,非常感谢补充。
    ETag 不影响 Freshness 验证,这个我赞同哈~
    文中说的只是:两者共存确实会影响最终的结果。

    “访问不同的网站会返回不同的 ETag 导致 304 失效,降级为200请求”,其实你想说的应该是“访问同一域名下部署在不同服务器上的同一文件,由于 ETag 不同,会多触发一次 HTTP 请求”吧?

    #3
  • hefangshi

    @vvian00 恩说错了,我改下

    我说的意思是

    1. 是否设置ETag,与请求是否会from cache直接获取没有关系。

    2. 设置ETag的目的是提供304请求的验证参数,而由于一些场景原因,ETag反而会破坏304验证。

    说描述不准确主要是针对开头的

    为什么有的缓存是 200 OK (from cache),有的缓存是 304 Not Modified 呢?很简单,看运维是否移除了 Entity Tag。移除了,就总是 200 OK (from cache)。没有移除,就两者会交替出现。

    #4
  • vvian00

    我这个开头为了吸引眼球。。。而且确实不存在逻辑错误

    如果设置了缓存,有的是 304 Not Modified ,有的 是 200 from cache ,确实就是这个原因没错。

    #5
  • ro87630872

    @hefangshi 求更多浏览器 以及 内核 性能相关分享

    #6
  • evantre

    题主的结论是不正确的

    #7
  • vvian00

    @evantre 你这种情况是有可能的。

    如果你没有做负载均衡,能保证同一个文件的 ETag 只有一个版本,那么就不用移除 ETag。

    就算你做了负载均衡且本次访问的 ETag 正好和上次一样,也会是 200(from cache)。

    就算你做了负载均衡,你几台服务器正好都能对同一个文件产生同一个版本的 ETag ,也可以是 200 (from cache)。

    但是如果你做不到上一条,那么最好是移除掉 ETag。

    全篇不是有说 ETag 就不能 200(from cache) 的意思哈。

    #8
  • 阿安

    @hefangshi 说的很全面和详细,赞一个!

    #9
  • ShinePaul

    @vvian00 我现在遇到的是做了负载均衡,有些文件的304的确失效了,不过仔细看了一下,有些文件是 由于不同的etag导致失效,有些是偶尔有etag,偶尔没有,两台服务器的配置是一样的,为什么会出现不同etag?为什么会出现没有etag的情况?

    #10
登录后回复,如无账号,请使用邀请码注册