Etag和If-None-Match:
Etag由服务器端生成,客户端通过If-None-Match这个条件请求来验证资源是否修改。请求一个文件的流程可能如下:
第一次请求:
1.客户端发起 HTTP GET 请求一个文件;
2.服务器处理请求,返回响应报文,响应头包括Etag(例如"2e681a-6-5d044840")(假设服务器支持Etag生成和已经开启了Etag).状态码200
第二次请求:
1.客户端发起 HTTP GET 请求一个文件,注意这个时候客户端同时发送的请求报文请求头包括If-None-Match,而它的值就是Etag的值(此处由发起请求的客户端来设置)。
2.服务器会判断客户端发送过来的If-None-Match值与服务器的Etag值是否与相同,如果相同,就把If-None-Match的值置为false,状态码置为304(未修改——Not Modified),响应体为空,返回响应报文,客户端将继续使用本地缓存。如果不相同,就将If-None-Match的值设为true,返回状态码为200,客户端重新解析服务器返回的数据。
问题来了,Etag和Last-Modified有什么区别?如果文件被修改了,那么修改时间也变了,我们可以通过判断修改时间来判断文件是否被修改,从而返回相应的状态码,那Etag是不是多余的?
Etag和Last-Modified区别:
Last-Modified包含了上次更改文档的日期。事实证明,在尝试确定文档是否已更改时,Last-Modified日期并不很可靠。有时开发人员会在修复某些内容后将所有文件上传到服务器,即使内容仅在子集上更改,也会重置所有文件的Last-Modified日期。为了适应这种情况,大多数服务器也会发送一个ETag。 ETag代表实体标记,并且是唯一的标识符,其仅根据文件的内容而改变。大多数服务器实际上使用像SHA256这样的散列函数来计算ETag。
Last-Modified与Etag类似。不过Last-Modified表示响应资源在服务器最后修改时间而已。与Etag相比,不足为:
(1)Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间。如果1s内文件被修改了,但是Last-Modified没变,此时不能根据Last-Modified判断文件是否被修改过。
(2)如果某些文件会被定期生成,有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存;
(3)有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形。
然而,Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。
ETag生成的常用方法包括使用资源内容的抗冲突散列函数生成的哈希值、最后修改时间戳的散列或甚至仅使用资源的版本号。 即ETag就是服务器生成的一个标记,用来标识返回值是否有变化,且Etag的优先级高于Last-Modified。
这样就能解释上面一个问题,当响应头既有Last-Modified又有Etag,Etag是多余的吗?
设想这样一种情景,资源A已经被请求过了,现在资源A又自动重新生成,仅仅只是最后修改时间Last-Modified变了,其余的内容均无变化,但是Last-Modified和之前不一样并不能说明资源A已经修改了,此时Etag的作用体现出来,根据资源计算的哈希值不变,说明资源无变化,返回响应报文的响应体为空,状态码304,客户端仍然使用缓存。这样利用客户端的缓存,可以节省服务器的带宽,因为服务器不需要每次都把全量数据返回给客户端。
我们可以随意打开一个带有图片的网站http://stevesouders.com/hpws/expiresoff.php,按F12看到NetWork选项后,刷新页面,第一次图片会去服务器请求。然后马上第二次刷新,看到一些图片文件的Size是from memory cache或者from disk cache,状态码为200, 直接是用的缓存,为什么可以直接用缓存呢?不去服务器请求吗?比如下图
因为响应报文的响应头中的Expires字段会告诉客户端这个资源的过期时间,在过期之前可以继续使用。
或者比如响应头中的字段:Cache-Control:max-age=315360000告诉浏览器这个资源可以被缓存315360000s=10年(当然这只是一个测试网站才这么做的,一般不会这么久)
带着这个问题,我们来学习几个知识点Expires和Cache-Control。
Expires和Cache-Control:
Expires:
这个字段是HTTP/1.0中的,Expires是需要在服务端配置(具体配置也根据服务器而定),Expires添加的是该资源过期的日期,浏览器会根据该过期日期与客户端时间对比,如果过期时间还没到,则会去缓存中读取该资源,如果已经到期了,则浏览器判断为该资源已经不新鲜要重新从服务端获取。通过这种方式,可以实现直接从浏览器缓存中读取,而不需要去服务端判断是否已经缓存,避免了这次http请求。值得注意的是Expires时间可能存在客户端时间跟服务端时间不一致的问题。所以,建议Expires结合Cache-Control一起使用,大型网站中一起使用的情况比较多见。
Cache-Control:
该字段是HTTP/1.1协议中的,可以是请求头中的或者响应头中的字段。它允许服务器控制客户端缓存收到的响应的方式和时长。 Cache-Control是一个复杂的野兽,具有许多内置功能。 99%的情况下,只需要“cacheability”(可缓存性)和“max-age”。但是Cache-Control可能被某些缓存和浏览器忽略。可以通过将Expires HTTP版本1.0标头字段值设置为早于响应时间的时间来进行模拟。Cache-Control相对于Expires更加具体,细致。若同时设置了Cache-Control和Expires,Cache-Control的优先级高于Expires。
下面就来看看,Cache-Control响应头中常用字段的具体含义:
(1)max-age:用来设置资源(representations)可以被缓存多长时间,单位为秒;
(2)s-maxage:和max-age是一样的,不过它只针对代理服务器缓存而言;
(3)public:指示响应可被任何缓存区缓存;
(4)private:只能针对个人用户,而不能被代理服务器缓存,不能被共享缓存处理;
(5)no-cache:请注意,no-cache不会指示浏览器或代理是否要缓存内容。它只是告诉浏览器和代理在使用它之前验证服务器的缓存内容(这是通过If-Modified-Since,If-Unmodified-Since,If-Match,If-None-Match属性完成的)。因此,发送无缓存值指示浏览器或代理仅仅基于缓存内容的“新鲜度标准”不使用缓存内容。防止旧内容在未经验证的情况下向用户显示的另一种常见方法是Cache-Control:max-age = 0。这会指示用户代理内容是陈旧的,并且应在使用前进行验证。所以no-cache和max-age=0是相同效果。Cache-Control:no-cache 也适用于客户端发出的请求。它是浏览器告诉服务器和任何中间缓存它想要新资源的一种手段。这个和HTTP / 1.0规范中定义的Pragma:no-cache头字段具有相同的目的。但是,它仅为请求标头定义。它没有指定它在响应头中的含义。大多数HTTP/1.0缓存不会识别或服从Cache-Control : no-cache指令。
(6)no-store:指示浏览器应用程序尽最大努力不将其写入磁盘(即不缓存它)。 不应缓存资源的请求并不保证它不会写入磁盘。特别是,HTTP / 1.1定义区分了历史存储和缓存。如果用户导航回上一页,浏览器仍可能会显示已存储在历史记录存储中的磁盘上的页面。根据规范,这是正确的行为。许多用户代理在从历史存储或缓存加载页面时显示不同的行为,具体取决于协议是HTTP还是HTTPS。用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
If-Modified-Since:
If-Modified-Since 和 Last-Modified 一样都是用于记录页面最后修改时间的 HTTP 头信息,只是 Last-Modified 是由服务器往客户端发送的 HTTP 响应头字段,而 If-Modified-Since 则是由客户端往服务器发送的请求头字段。当再次请求本地存在的 cache 页面时,客户端会通过 If-Modified-Since 字段将先前服务器端发过来的 Last-Modified 最后修改时间戳发送回去,这是为了让服务器端进行验证,通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回新的内容,如果是最新的,则返回 304 和空响应体告诉客户端其本地 cache 的页面是最新的,于是客户端就可以直接从本地加载页面了,这样在网络上传输的数据就会大大减少,同时也减轻了服务器的负担。
If-Modified-Since如果提供的日期以来尚未更改,服务器不会发送文档的实际内容。 如果文档的ETag仍然与If-None-Match标头的值匹配,则服务器将不发送实际文档。 If-None-Match和If-Modified-Since都可以出现在同一个请求中,但ETag优先于If-Modified-Since(就是Last-Modified的时间戳的值),因为它被认为更准确(Etag显然比Last-Modified值准确)。
参考文章:
https://www.jianshu.com/p/f331d5f0b979
https://www.cnblogs.com/softidea/p/5986339.html