There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton
Web 开发的优化阶段中,缓存是必不可少的一个因素,而其中 HTTP 缓存机制是 Web 性能优化的一个重要手段,善于利用 HTTP 缓存,能够大大提升 Web 页面的访问速度。
强缓存与协商缓存
在浏览器的缓存策略里面,我们常讨论的有两种缓存机制,一种叫 强缓存,也称为本地缓存,另一种叫 协商缓存。浏览器根据 HTTP 响应头里面的内容,来采取对应的缓存方式。
强缓存与协商缓存的区别在于资源的读取方式,浏览器在首次访问 Web 应用相关的资源之后,第二次访问资源时:
1、如果该资源命中强缓存,浏览器直接从本地缓存中取,返回 200。本次请求不会与服务端进行通信
2、如果该资源在上次响应中携带了协商缓存相关的头,浏览器首先向服务端确认缓存是否有效,如果有效,服务端返回 304,携带相关缓存头但不返回资源内容,浏览器直接使用本地缓存;如果无效,服务端返回新的资源内容
强缓存
强缓存相关的 HTTP header 有 Expires 和 Cache-Control。
Expires
Expires 是 HTTP 1.0 出现的响应头,它的值为 GMT 格式的时间字符串,如 Expires: Sun Dec 24 2017 17:01:30 GMT
,这个时间代表资源的过期失效时间,在该时间之前,浏览器始终使用强缓存。Expires 的主要缺点在于这个时间是一个绝对时间,当服务端与客户端时间偏差较大时,会导致缓存混乱。
Cache-Control
Cache-Control 是 HTTP 1.1 出现的头,它可以用在请求头和响应头中,该指令是单向的,意味着在请求中设置的指令,在响应中可能没有。常用的值有 max-age
、no-cache
和 no-store
。
max-age 为一个键值对,值为一个相对时间,如 Cache-Control: max-age=60
,代表该资源缓存的有效时间为 60 秒,60 秒之内使用强缓存
- no-cache 代表不使用强缓存,而使用协商缓存
- no-store 不使用缓存,每次必须重新从服务器获取
Cache-Control 的其他值如下:
Cache-directive | 说明 |
---|---|
public | 所有内容都将被缓存(客户端和代理服务器都可缓存) |
private(默认) | 内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存) |
no-cache | 必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。 |
no-store | 所有内容都不会被缓存到缓存或 Internet 临时文件中 |
must-revalidation/proxy-revalidation | 如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证 |
max-age=xxx (xxx is numeric) | 缓存的内容将在 xxx 秒后失效, 这个选项只在HTTP 1.1可用, 并如果和Last-Modified一起使用时, 优先级较高 |
需要注意的是,如果同时存在 Expires 和 Cache-Control,Cache-Control 的优先级要高于 Expires。
协商缓存
协商缓存相关的头有:Last-Modified / If-Modified-Since、ETag / If-None-Match。这两个搭档都是成对出现,左侧为响应头,右侧为请求头。
Last-Modified / If-Modified-Since
Last-Modified 代表资源的 修改时间,是一个 GMT 格式的时间字符串,如:Last-Modified: Wed, 26 Apr 2017 00:40:48 GMT
。服务端返回资源时,如果携带 Last-Modified,则浏览器后续所有对该资源的请求头里都会携带 If-Modified-Since,值与服务器上次返回的 Last-Modified 值一致。服务器根据该值判断资源是否有修改,如果没有,返回 304,body 中没有内容;如果有,返回 200,body 中为修改后的资源内容。
ETag / If-None-Match
ETag 代表资源的 内容,是 Entity Tag(实体标签)的缩写,该值为资源的唯一标识符。HTTP 1.1 协议并没有规范该值如何生成,一般而言为该资源的散列值。当浏览器访问该资源时,服务器会根据资源计算出一个哈希值,并随 ETag 响应头返回,如 ETag: W/"76601-15ba7b41280"
,当浏览器下次需要访问资源时,携带 If-None-Match: W/"76601-15ba7b41280"
请求头,服务器再次计算该资源的 ETag 值,如果没有改变则返回 304,如果改变返回新的资源内容。
Last-Modified 与 ETag
Etag 主要为了解决 Last-Modified 无法解决的一些问题。
1、一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了;
2、某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1 秒内修改了 N 次),Last-Modified 能检查到的粒度是秒级的,这种修改无法判断(或者说 UNIX 记录 MTIME 只能精确到秒)
3、某些服务器不能精确的得到文件的最后修改时间
总的来说,ETag 是 LastModifed 的补充,比 LastModified 更加严谨。但设定了 Etag之后,每次客户端发出请求,服务端都会根据资源重新生成一个 ETag,相对来说,对性能会有影响。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。