HTTP的缓存实践
参考资料
缓存的资料中,难免有一些描述不精确的地方,会误导人,需要自己结合更多的靠近规范的解释去甄别,下述两篇中,第一篇写的非常好,第二篇可以作为辅助:
前情
首部字段分为:通用首部、请求首部、响应首部、实体首部。
目前主流的HTTP版本是HTTP/1.1,部分网站的版本已经是HTTP/2。
字段
Pragma
-
是HTTP/1.0的通用首部字段
-
仅有一个no-cache的值
-
含义是使用之前必须去服务器校验当前版本是不是最新的
-
这个字段的作用对象是服务器,因为Pragma is not specified for HTTP responses ,只能在请求头传递给服务器
-
这个字段的控制权归浏览器,即浏览器作为发起者,决定了是否在请求头部是否加上该字段
-
该字段已经被抛弃,但为了向下兼容还是保留了它
Expires
-
是HTTP/1.0的实体首部字段
-
值是GMT格式的时间值
-
含义是资源到什么时间之前都是有效的
-
这个字段的作用对象是浏览器,因为The HTTP Expires header is a response-type header,通常作为相应头传递给浏览器
-
这个字段的控制权是服务器,即服务器发送给浏览器,浏览器依据它来决定重新请求网页时,是否发送新的请求到服务器
Cache-Control
-
是HTTP/1.1的通用首部字段
-
有很多可选值
-
可以用于控制资源(1)是否缓存、(2)缓存时间、(2)过了缓存如何校验
-
它即出现在请求报文中,也可以出现在响应报文中
-
响应报文中可以出现的值和含义包括
- 是否可缓存,public,private,no-store,no-cache
- public,所有用户在通过缓存服务器的时候,都可以缓存资源
- private,不允许代理服务器使用缓存,而是存储在本地,参考只被允许本地机器使用 和 浏览器缓存策略之扫盲篇 ,适合存储带个人特征不会被他人使用的信息
- no-store 不许缓存
- no-cache 可以缓存但是必须走服务器校验资源是最新的
- 缓存有效时间,max-age, s-maxage
- max-age,资源的新鲜时间出创建响应开始
- s-maxage,针对代理服务器,跟max-age保持一致即可
- 缓存失效后校验,must-revalidate,proxy-revalidate
- must-revalidate,失效后必须要去服务器校验后才能使用
- proxy-revalidate,针对代理服务器的校验
- 其他字段,no-transform
- no-transform,代理服务器为了减少资源缓存大小,会进行压缩并变更头信息,加上该字段禁止如此
- 是否可缓存,public,private,no-store,no-cache
-
请求报文中的值和含义包括
- 是否可缓存,no-store,no-cache
- no-store 同
- no-cache 同
- 缓存有效时间,max-age, min-fresh, max-stale
- max-age,同
- s-maxage,同
- min-fresh,获取一个能在指定的秒数内保持其最新状态的响应
- max-stale,客户端愿意接收一个已经过期的资源,过期时间不能超过多久
- 其他字段,no-transform, only-if-cached
- no-transform,同
- only-if-cached,希望拿到的是已经被储存起来的响应
- 是否可缓存,no-store,no-cache
Last-Modified 和 ETag
-
Last-Modified是实体首部字段,ETag是响应首部字段
-
他们都是在响应头出现
-
其中Last-Modified描述了资源最后一次修改时间,ETag(Entity Tag)描述了资源的唯一标识
-
ETag比Last-Modified更精确,因为文件可能在同一秒内被修改了Last-Modified是同一个值,同样修改多次可能内容不变
-
ETag又分为强弱两种模式,强模式字符级别完全相同,弱模式的值前面有个
w/
的标记,标识语义上没有变化
If-Modified-Since 和 If-None-Match
-
If-Modified-Since 和 If-None-Match 都是请求首部字段
-
他们会作请求头传递给服务器,值对应的是 Last-Modified 和 ETag 的值
-
服务器会基于最新的生成的值与If-Modified-Since/If-None-Match的值进行对比,资源过期返回200,否则返回304 Not Modified。
If-Unmodified、If-Match、If-Range
- Stackoverflow关于仨字段的讨论并不常用,暂且略过
缓存策略
个人博客为例
博客内容是基于Webpack打包的
首次打开网页,第一个请求访问的是html文件,通常来说响应头不会指定Cache-Control:max-age=xxx 或 Expires 这样的字段。
因为网页作为入口文件,必须是最新的,也就是说,不能用强缓存,而是返回ETag 和 Last-Modified。
相应的,图片、js、CSS文件也都是在请求头通过ETag 和 Last-Modified来进行缓存控制的。
在地址栏点击刷新,请求html的时候默认会携带 If-Modified-Since 和 If-None-Match给服务器。
服务器校验发现本地缓存的版本是最新的,返回304,直接利用使用本地的缓存内容即可。
相应的,本地的被依赖的图片、JS、CSS,也会因为HTML使用的是缓存而使用缓存,因此是200 from memory cache 或者 200 from disk cache。
- 200 from memory cache:资源在内存当中,一般脚本、字体、图片会存在内存当中
- 200 from disk cache:在磁盘当中,一般非脚本会存在内存当中,如css等
浏览器的刷新行为
- 浏览器地址前进、后退,在入口的html文件这一级,就会使用强缓存,200 from disk cache
- 浏览器地址栏回车 或 点击刷新按钮,Command+R(Mac下)刷新,入口文件(注资源)会走协商缓存,派生资源优先强缓存
- Command+Shift+R 刷新,所有资源都会从服务器请求最新的
补充:
最佳推荐:浏览器缓存策略之扫盲篇明确分类了上述三种行为对应的操作,并且介绍了在Chrome和Firefox上的差异,感兴趣可以实践。
最佳实践
- 不要缓存 HTML,避免缓存后用户无法及时获取到更新内容。
- 使用Cache-Control和ETag来控制 HTML 中所使用的静态资源的缓存。一般是将Cache-Control的max-age设成一个比较大的值,然后用ETag进行验证。
- 使用签名或者版本来区分静态资源。这样静态资源会生成不同的资源访问链接,不会产生修改之后无法感知的情况。
网友评论