HTTP 协议的缓存机制概述

HTTP 协议的缓存机制涉及到多个请求头字段,而且整个缓存机制的细节行为也存在各种情况的差异,譬如说什么时候访问本地缓存不发送请求,什么时候发送请求查看资源是否更新,获取 response 什么情况下更新缓存等。以前我对此一知半解只是笼统的知道一些概念,譬如 Cache-Control 可以控制缓存的时间和是否需要缓存,但是缓存过期后的行为,有缓存后浏览器是否有 http 请求都不甚了解。所以特地 google 下,此篇是对此的知识梳理。

协议概述

什么情况下可以使用本地缓存?譬如说我们用 get 方式请求了一个资源 http://mytest.domain.com/static/images/bg.png,那么我们下次再请求这个图片资源的时候符合哪些条件可以使用本地缓存呢?

  • url 必须是 http://mytest.domain.com/static/images/bg.png,如果是 http://mytest.domain.com/static/images/bg.png?t=12312321 就会发起新的请求,因为 url 不同。
  • 发送请求的 method 必须可被缓存,譬如 get。
  • 第一次请求 response(即本地缓存)如果有一个 Vary 头,他的值列出的是一系列 http header,第二次请求的请求头中那些在 Vary 值中所列的头,必须和第一次请求相同(具体规则)。
  • 第二次请求不包含请求头 Pragma: no-cache
  • 第二次请求不包含请求头 Cache-Control: no-cache|max-age=0
  • 第一次请求的 response(即本地缓存)不包含 Cache-Control: no-cache
  • 本地缓存没有过期
  • 或者虽然本地缓存已经过期,但是服务器验证缓存和服务器资源一致,允许使用本地缓存的情况(即获得304 response)

注1:上述任何提一个条件都可以被 cache-control extension 覆盖

注2:response header中不仅仅可以 Cache-Control: no-cache,还可以 Cache-Control: no-cache="Set-Cookie" 详见

如何计算本地缓存是否过期

浏览器是通过比较缓存剩余有效时间和当前缓存已存在时间来判断的:response_is_fresh = (freshness_lifetime > current_age)freshness_lifetime 取值优先级次序如下列表所示(排在上面的优先级越高):

  1. Cache-Control: s-maxage=xx
  2. Cache-Control: max-age=xx
  3. Expires: xxxxx
  4. 按规则进行计算(推测)

注1;如果有多个重复的上述头,那么是非法的,视作 response(资源)过期

freshness_lifetime 计算(推测)规则:

规范并没有给出具体的算法,但是给出了最坏情况(but does impose worst-case constraints on their results),如果 response(资源)有 Last-Modified 头,那么推荐用从当前到lastmodified这个时间段的 10% 作为 freshness_lifetime,并且 response(资源)的 current_age 如果已经超过24小时,必须在这个 response 上加上113 warn-code头。很少有浏览器实现了freshness_lifetime的自助计算(推测),所以还是还是鼓励给出上述显式的1 - 3三种情况。

current_age 计算规则:

泛泛来说就是 response(资源)在本地的驻足时间加上网络传输时间,因为网络传输时间的计算有多个条件,规范实在看的我头晕,所以具体计算规则详见规范

本地缓存过期,如何通过服务器验证缓存的是否依然有效(即304的情况)

首先,如果第一次的 response(资源)头中显示申明了一些禁止缓存的头(譬如:"no-store" or "no-cache" 等等),就不存在过期不过期的问题,因为这个资源不允许缓存。其次,过期缓存也不一定不可用,如果在断网或者第二次请求带上 max-stale 这个请求头,那么浏览器可以使用过期的缓存(masx-stale 表示在缓存过期后多少时间内浏览器依然可以使用缓存)。碰到过期缓存,浏览器可以发送一个条件请求(conditional request)。这个请求的 url 依然是第一次请求的 url,只是会带上些当前资源的一些信息,以供服务器验证这个缓存是否依然可用还是需要更新:

  1. response(资源)的 Last-Modified 头所带的值会放到条件请求的 If-Modified-Since 头中,或者是 If-Unmodified-Since 又或者 If-Range。
  2. response(资源)的 ETag 头所带的值会放到条件请求的 If-None-Match 头中,或者 If-Match 又或者 If-Range。

服务器会根据不同的条件请求头来验证资源,具体的行为详见规范,这里不细致展开。针对条件请求,服务器返回会有三种情况:

  1. 一个带有304 status code 的返回。表示缓存可以被更新和重用。
  2. 一个带有 body 的完整的 response。表示用这个 response 作为请求的返回,并且视条件可以用这个完整的 response 替换浏览器原有的缓存(此资源)。
  3. 如果返回一个5xx的 response,那么浏览器可以选择就显示这个5xx的返回,或者使用本地缓存(尽管可能是过期的)- 规范没有规定应该选择哪种处理,应该是取决于浏览器的行为。

如果是一个304的返回,规范说这个返回可以更新本地缓存,更新策略分三种:

  1. 如果这个304 response 带有资源有效性的强验证头,那么浏览器会寻找本地缓存,寻找那些带有同样强验证头的缓存,然后用这个最新的 response 去更新这些匹配的缓存(同一个资源可能浏览器保存有多份缓存,譬如日期不同等)。
  2. 如果这个304 response 带有资源有效性的弱验证头,那么浏览器同样会找相匹配的缓存,但是只会更新最新的那条匹配的缓存。
  3. 如果这个304 response 没有带有任何资源有效性验证头,并且浏览器缓存只有一份,并且这份也同样没带有任何资源有效性验证头,那么浏览器就会用这个304 response,更新本地缓存。

协议流程图

假设第一次请求一个资源,返回 header 里面带上如下字段:
Cache-Control: max-age=600
Last-Modified: Wed, 28 Aug 2013 10:36:42 GMT
ETag: "124752e0d85461a16e76fbdef2e84fb9"

抛开细枝末节的东西,那么第二次请求通常大致流程图如下:

                  当前资源缓存是否过期:response_is_fresh = (freshness_lifetime > current_age)
                                            |
                               -----------------------------------
                              |                                   |        
                              是                                  否
                              |                                   |
                  发送请求,带上请求头                          从本地缓存中获取资源(不发请求)
                  If-Modified-Since: 此资源Last-Modified的值      
                  If-None-Match: 此资源ETag的值
                              |
                  服务器根据 If-Modified-Since 和 If-None-Match
                  两个值判断资源是否更新过
                              |
                  -------------------------
                 |                         |
                 是                        否
                 |                         |
返回一个 status code:200 的 response    返回一个 status code:304 的 response
response body 里面是请求的资源           response body 为空
                 |                         |
浏览器用 response body里面的资源         依然从本地缓存里面获取资源
替换本地缓存中的资源

特殊情况

当你去浏览器验证的时候可能会碰到一些特殊情况,就是缓存有效,但是你刷新浏览器依然发送的条件请求。其实是因为浏览器在请求头中加入了一些料,譬如: Cache-Control: max-age=0。你刷新的方式可以有很多种,譬如:按F5,按ctrl+F5,在地址栏按回车等等。这些不同的行为都会影响浏览器发送请求的行为。这里有一些参考《在浏览器地址栏按回车、F5、Ctrl+F5刷新网页的区别

参考资料

rfc7234

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • http协议有http0.9,http1.0,http1.1和http2三个版本,但是现在浏览器使用的是htt...
    一现_阅读 1,855评论 0 3
  • 一、概念(载录于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434阅读 8,326评论 6 152
  • Http协议详解 标签(空格分隔): Linux 声明:本片文章非原创,内容来源于博客园作者MIN飞翔的HTTP协...
    Sivin阅读 5,201评论 3 82
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 针对浏览器的http缓存的分析也算是老生常谈了,每隔一段时间就会冒出一篇不错的文章,其原理也是各大公司面试时几乎必...
    全端玩法阅读 873评论 0 9