图和代码来源理解HTTP缓存和304状态码
下图是使用
express
启动了一个服务,启动之后浏览器访问两次localhost:8080
所得请求、响应头
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('hello world');
});
app.listen('8080')
第一次请求
第二次请求
从上面两次请求的请求和响应头发现,第二次请求localhost:8080
时服务器返回的是304
状态码。
之前了解过304
状态码是当浏览器请求未改变且已缓存的资源时,服务器会返回304
,告知浏览器,该资源从某个时间之后没有再改变,可以用在浏览器端缓存的资源。但具体304
相关字段并未研究过,下来我们来看下304
的相关内容。
1. HTTP缓存
web开发的优化阶段中,缓存是必不可少的一个因素,而其中HTTP缓存机制是Web性能优化的一个重要手段,善于利用HTTP缓存,能够大大提升Web页面的访问速度
2. 强缓存与协商缓存
强缓存:本地缓存
当浏览器二次请求服务器资源时,浏览器直接从本地缓存中读取并返回200,不会与服务器进行交互
协商缓存:浏览器根据HTTP响应头里面的内容,来采取对应的缓存方式
如果用户访问的资源在上次响应中携带了协商缓存相关的头,浏览器会携带相关字段并向服务端确认缓存的资源是否有效,如果服务端判断该资源有效,则返回304,携带相关缓存头但不返回资源内容,浏览器直接使用本地缓存。否则返回200,服务器返回新的资源内容。
3. 强缓存
强缓存相关
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
代表该资源的有效时间为60s
,在这60s
内浏览器使用强缓存。如果该值与Last-Modified
一起使用,该值优先级较高。no-cache
:==代表不使用强缓存,而是使用协商缓存。==代表浏览器在请求资源的时候必须先与服务器确认资源是否被更改,然后才能决定是否使用该资源。no-store
:==代表不适用缓存,每次都要从服务器重新获取。==所有内容都不会被缓存。public
:所有内容都会被缓存,客户端和代理服务器都可以缓存。private
:内容只能缓存在私有缓存中,即只有客户端可以缓存该资源,代理服务器不可以。
注意:如果同时存在expires
和Cache-Control
,则Cache-Control
优先级更高
4. 协商缓存
协商缓存相关头有:
Last-Modified/If-Modified-Since
、ETag/If-None-Match
。这两个相关头时成对出现的,前面的均为响应头,后面的为请求头。
Last-Modified/If-Modified-Since
Last-Modified
代表的是资源的最后修改时间,是一个GMT
格式的时间。服务器在返回资源时,如果写到Last-Modified
,则浏览器在后续对该资源的访问的请求头里都会携带If-Modified-Since
,值与服务器返回的Last-Modified
值相同。服务端通过该值来判断浏览器请求的该资源是否有修改,如果有,则返回修改后的内容,且返回200,否则返回304,body
中没有内容。
ETag/If-None-Match
ETag
代表资源的内容,该值为资源的唯一标识符,HTTP
没有规范该值如何生成,一般而言为该资源的散列值。当浏览器访问该资源时,服务端会根据资源计算出它的散列值(每一次都会重新计算),将计算出来的散列值与浏览器传来的值(If-None-Match
)进行对比,如果相同则返回304,否则返回200且将新的资源的内容放入body
中返回。- 用来标识一个资源。在具体实现中,
ETag
可以是资源的hash
值,也可以是内部维护的版本号。总的来说,ETag
是一个能够唯一标识一个资源的值且它能反映出资源内容的变化(当ETag
是资源的hash
值时,若服务端的资源和浏览器端缓存的资源不一致,则ETag
是不一致的,否则是相同的。当ETag
是版本号时,当服务端的资源和浏览器端缓存的资源不一致时,ETag
的取值也不一致,因为该资源对应的版本号是不同的)- 当浏览器端第二次请求服务器时,利用
If-None-Match
来告知服务器,请求的这个资源在浏览器端有缓存,且它的ETag
为***。如果服务器上的资源没有变化,即服务器上的该资源的ETag
和第二次请求发送的If-None-Match
值相同,则服务端会返回304
响应。告知浏览器该资源没有改变,可以直接调用本地缓存。
5. Last-Modified
和ETag
ETag
主要是为了解决Last-Modified
不能解决的问题。- 一些文件也许会周期性地改变,但是实际上它的内容不会改变,只会改变它在服务器端的最后修改时间,此时当浏览器端发来资源请求时,最后修改时间和服务器端的不一致。
- 某些文件修改的非常频繁,比如在秒以下的时间内进行修改。而
Last-Modified
可检查到的粒度是秒级的,如果修改的特别频繁,是无法通过Last-Modified
检测出来资源的变化的。- 某些服务器不能精确地得到文件的最后修改时间
ETag
是Last-Modified
的补充,比Last-Modified
更加严谨。但是ETag
相对来说对性能会有影响。每次浏览器端将If-None-Match
发送至服务端时,服务端都会对请求的资源进行散列值计算,然后与浏览器发来的值进行对比。每一次计算会对性能有一定的影响。
部分参考HTTP缓存