来源:吴秦
HTTP缓存策略
WEB中缓存的几种方式
HTTP的请求头标签If-Modified-Since
Http1.0 - Expires
Http1.1 - Etag
Cache-control
cache-control优先级高于Expires
下面两种都需配合cache-control使用,服务器优先验证Etag
1、Last-Modified、If-Modified-Since
2、Etag、If-None-Match
注意:
通过Last-Modified,If-Modified-Since方式和服务器通信,客户发出http请求中包含If-Modified-Since,如果服务器端代码没有修改,服务器端返回304响应代码的请求响应头(内容不返回),客户端则直接用本机缓存的内容缓存显示结果。相当于节省了服务器执行代码时间以及数据传输时间。
通过Expires ,Cache-Control (max-age)控制,客户端发现如果上次请求的页面还未过期,通过Expires或者Cache-Control进行辨别,则直接显示本机缓存的内容,不与服务器进行通信。
Http协议的cache-control的常见取值及其组合释义:
no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器.
no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源)
private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.
public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等
max-age: 相对过期时间, 即以秒为单位的缓存时间.
no-cache, private: 打开新窗口时候重新访问服务器, 若设置max-age, 则缓存期间不访问服务器.
private, 正数的max-age: 后退时候不会访问服务器
no-cache, 正数的max-age: 后退时会访问服务器
点击刷新: 无论如何都会访问服务器.
==========================================================================
-------------------------------------------------------
浏览器一般缓存图片、CSS、JS等静态文件,因为这些文件的更新频率相对来说比较低,合理利用浏览器的缓存对网站的性能提升有很大帮助。
HTTP缓存分为两部分,分别是本地缓存和缓存协商,当本地缓存不生效时会启用缓存协商。HTTP缓存主要由HTTP协议的头(Header)信息来制定。
本地缓存
本地缓存是指当浏览器请求资源时,如果命中了浏览器本地的缓存资源,那么浏览器就不会发送真正请求给服务器。它的执行过程是:
1. 第一次浏览器发送请求给服务器时,此时浏览器还没有本地缓存副本,服务器返回资源给浏览器,响应码是200 OK,浏览器收到资源后,把资源和对应的响应头一起缓存下来。
2. 第二次浏览器准备发送请求给服务器时候,浏览器会先检查上一次服务端返回的响应头信息中的Cache-Control(它的值是一个相对值,单位为秒,表示资源在客户端缓存的最大有效期),过期时间为第一次请求的时间加上Cache-Control的值,过期时间跟当前的请求时间比较,如果本地缓存资源没过期,那么命中缓存,不再请求服务器。
3. 如果没有命中,浏览器就会把请求发送给服务器,进入缓存协商阶段。
与本地缓存相关的头有:Cache-Control、Expires,Cache-Control有多个可选值代表不同的意义,而Expires就是一个日期格式的绝对值。
CACHE-CONTROL
Cache-Control是HTTP缓存策略中最重要的头信息,它是HTTP/1.1中出现的,它由如下几个值 no-cache:不使用本地缓存。需要使用缓存协商,先与服务器确认返回的响应是否被更改,如果之前的响应中存在ETag,那么请求的时候会与服务端验证,如果资源未被更改,则可以避免重新下载。 no-store:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。 public:可以被所有的用户缓存,包括终端用户和CDN等中间代理服务器。 private:只能被终端用户的浏览器缓存,不允许CDN等中继缓存服务器对其缓存。 * max-age:从当前请求开始,允许获取的响应被重用的最长时间(秒)。
例如:
Cache-Control:public, max-age=1000
表示资源可以被所有用户以及代理服务器缓存,最长时间为1000秒。
EXPIRES
Expires是HTTP/1.0出现的头信息,同样是用于决定本地缓存策略的头,它是一个绝对时间,时间格式是如Mon, 10 Jun 2015 21:31:12 GMT,只要发送请求时间是在Expires之前,那么本地缓存始终有效,否则就会去服务器发送请求获取新的资源。如果同时出现Cache-Control:max-age和Expires,那么max-age优先级更高。他们可以这样组合使用:
Cache-Control:public
Expires: Mon, 10 Jun 2015 21:31:12 GMT
缓存协商
当第一次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过期抑或它的属性设置为no-cache时,那么浏览器第二次请求时就会与服务器进行协商,询问浏览器中的缓存资源是不是旧版本,需不需要更新,此时,服务器就会做出判断,如果缓存和服务端资源的最新版本是一致的,那么就无需再次下载该资源,服务端直接返回304 Not Modified 状态码,如果服务器发现浏览器中的缓存已经是旧版本了,那么服务器就会把最新资源的完整内容返回给浏览器,状态码就是200 Ok,那么服务端是根据什么来判断浏览器的缓存是不是最新的呢?其实是根据HTTP的另外两组头信息,分别是:Last-Modified/If-Modified-Since 与 ETag/If-None-Match。
LAST-MODIFIED与IF-MODIFIED-SINCE
浏览器第一次请求资源时,服务器会把资源的最新修改时间Last-Modified:Thu, 29 Dec 2011 18:23:55 GMT放在响应头中返回给浏览器,第二次请求时,浏览器就会把上一次服务器返回的修改时间放在请求头If-Modified-Since:Thu, 29 Dec 2011 18:23:55发送给服务器,服务器就会拿这个时间跟服务器上的资源的最新修改时间进行对比,如果两者相等或者大于服务器上的最新修改时间,那么表示浏览器的缓存是有效的,此时缓存会命中,服务器就不再返回内容给浏览器了,同时Last-Modified头也不会返回,因为资源没被修改,返回了也没什么意义。如果没命中缓存则最新修改的资源连同Last-Modified头一起返回。
第一次请求返回的响应头:
Cache-Control:max-age=3600
Expires:Tue, 26 Jan 2016 08:28:52 GMT
Last-Modified:Fri, 15 Jan 2016 12:06:06 GMT
第二次请求的请求头信息:
If-Modified-Since:Fri, 15 Jan 2016 12:06:06 GMT
这组头信息是基于资源的修改时间来判断资源有没有更新,另一种方式就是根据资源的内容来判断,就是接下来要讨论的ETag与If-None-Match
ETAG与IF-NONE-MATCH
ETag/If-None-Match与Last-Modified/If-Modified-Since的流程其实是类似的,唯一的区别是它基于资源的内容的摘要信息(比如MD5 hash)来判断。浏览器发送第二次请求时,会把第一次的响应头信息ETag的值放在If-None-Match的请求头中发送到服务器,与最新的资源的摘要信息对比,如果相等,取浏览器缓存,否则内容有更新,最新的资源连同最新的摘要信息返回。用ETag的好处是如果因为某种原因到时资源的修改时间没改变,那么用ETag就能区分资源是不是有被更新。
第一次请求返回的响应头:
Cache-Control:public, max-age=31536000
ETag: "15f0fff99ed5aae4edffdd6496d7131f"
第二次请求的请求头信息:
If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"
===============================================================
浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires; Cache-control等)。但是也有非HTTP协议定义的缓存机制,如使用HTML Meta 标签,Web开发者可以在HTML页面的节点中加入标签,代码如下:
html code |
|
上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。
下面我主要介绍HTTP协议定义的缓存机制。
Expires策略
Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。
下面是宝宝PK项目中,浏览器拉取jquery.js web服务器的响应头:
注:Date头域表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date: Mon,31 Dec 2001 04:25:57GMT。
Web服务器告诉浏览器在2012-11-28 03:30:01这个时间点之前,可以使用缓存文件。发送请求的时间是2012-11-28 03:25:01,即缓存5分钟。
不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。
Expires 表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同max-age的
效果。但是如果同时存在,则被Cache-Control的max-age覆盖。
Cache-control策略(重点关注)
Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
http协议头Cache-Control : |
值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age 各个消息中的指令含义如下:
|
还是上面那个请求,web服务器返回的Cache-Control头的值为max-age=300,即5分钟(和上面的Expires时间一致,这个不是必须的)。
Last-Modified/If-Modified-Since
Last-Modified/If-Modified-Since要配合Cache-Control使用。
l Lalast-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
l If-If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
Etag/If-None-Match
Etag/If-None-Match也要配合Cache-Control使用。
l EtEtag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
l If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
既生Last-Modified何生Etag?
你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
l Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
l 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存
l 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
用户行为与缓存
浏览器缓存行为还有用户的行为有关!!!
用户操作 |
Expires/Cache-Control |
Last-Modified/Etag |
地址栏回车 |
有效 |
有效 |
页面链接跳转 |
有效 |
有效 |
新开窗口 |
有效 |
有效 |
前进、后退 |
有效 |
有效 |
F5刷新 |
无效 |
有效 |
Ctrl+F5刷新 |
无效 |
无效 |