浏览器展现页面的耗时很大程度决定于静态资源的加载速度。那我们如何提高资源加载的速度呢,方法有很多,但我们这里只关心浏览器可以做的事,那就是浏览器本身具有的缓存策略。
那如何控制浏览器的缓存策略呢?答案就在http协议返回的消息头字段,Cache-Control 和 Expires。
什么是cache-control?
Cache-Control header was defined as part of the HTTP/1.1 specification and supersedes previous headers (e.g. Expires) used to define response caching policies. All modern browsers support Cache-Control, hence that is all we will need.
简言之,cache-control 就是定义资源文件被缓存的时间和方式的一个http头。
Cache-Control 基础
Cache-Control 定义了缓存的时间和方式,
Cache-Control: max-age=2592000, public
但浏览器加载一个资源时,会收到许多HTTP头。当Cache-Control包括在其中时,浏览器将按照里面的值来缓存资源。如果Cache-Control定义一个文件需要被缓存五分钟,则五分钟内,对该文件的重复请求将只读取缓存。
什么是 max-age?
- "max-age"定义了缓存的时长
- "max-age"表示在特定的秒数只能,资源将是不变的
max-age 的使用
max-age部分在头部是这样的:max-age=2592000,单位是秒。缓存时间最大只支持1年,大于这个时间可能被忽略,详询RFC。
一些常用的值:
- 一分钟: max-age=60
- 一小时: max-age=3600
- 一天: max-age=86400
- 一周: max-age=604800
- 一个月: max-age=2628000
- 一年: max-age=31536000
当使用max-age去定义缓存时间时,必须考虑文件的类型和使用方式。
缓存指令
缓存指令部分在头部是这样的:
public
上面的Cache-Control头声明“public”。这表示这个文件将被以公开文件缓存(跟私有文件相反)
默认的,大多数文件会是公开的可缓存的,但是有时候对于敏感文档、安全性以及用户特有内容,公开缓存是不建议的。
有四种主要的指令
- public
- private
- no-cache
- no-store
public
"public"指令指服务器返回不仅可以被浏览器缓存,而且可以被中间代理缓存,后续其他用户请求该资源,都可以直接使用该的缓存。
官方定义如下
The "public" response directive indicates that any cache MAY store the response, even if the response would normally be non-cacheable or cacheable only within a private cache.
大体上,如css、js这些公共资源需要使用public缓存起来,从而加快页面加载。
private
private 指令表明,服务器返回只能被用户的浏览器缓存。官方定义如下:
Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache. This allows an origin server to state that the specified parts of theresponse are intended for only one user and are not a valid response for requests by other users. A private (non-shared) cache MAY cache the response.
no-cache
该指令如果没有指定了一个字段名,那么缓存每次都需要被验证是否未变更,验证通过则返回304,直接使用缓存。如果指定了字段名,则后续请求只须验证该字段。
If the no-cache directive does not specify a field-name, then a cache MUST NOT use the response to satisfy a subsequent request without successful revalidation with the origin server. This allows an origin server to prevent caching even by caches that have been configured to return stale responses to client requests.If the no-cache directive does specify one or more field-names, then a cache MAY use the response to satisfy a subsequent request, subject to any other restrictions on caching. However, the specified field-name(s) MUST NOT be sent in the response to a subsequent request without successful revalidation with the origin server. This allows an origin server to prevent the re-use of certain header fields in a response, while still allowing caching of the rest of the response.
no-store
该指令表明,服务器返回任何时候都不能被缓存,这主要是为了保护敏感数据。但这种方式来保护敏感信息是不可靠的。
The purpose of the no-store directive is to prevent the inadvertent release or retention of sensitive information (for example, on backup tapes). The no-store directive applies to the entire message, and MAY be sent either in a response or in a request. If sent in a request, a cache MUST NOT store any part of either this request or any response to it. If sent in a response, a cache MUST NOT store any part of either this response or the request that elicited it. This directive applies to both non- shared and shared caches. "MUST NOT store" in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it.Even when this directive is associated with a response, users might explicitly store such a response outside of the caching system (e.g., with a "Save As" dialog). History buffers MAY store such responses as part of their normal operation.The purpose of this directive is to meet the stated requirements of certain users and service authors who are concerned about accidental releases of information via unanticipated accesses to cache data structures. While the use of this directive might improve privacy in some cases, we caution that it is NOT in any way a reliable or sufficient mechanism for ensuring privacy. In particular, malicious or compromised caches might not recognize or obey this directive, and communications networks might be vulnerable to eavesdropping.
哪些文件需要被缓存?
图片、css、ico、js
如何清除缓存
添加文件指纹 比如main.js被缓存了,我们可以更改文件名为main_1.js达到更新缓存的目的。
其他缓存方法
Expires
Expires头指定缓存的时间点,例如2016年11月11日。在此之前的资源的请求将直接读取缓存。类似于max-age,只不过max-age指定的是缓存的时长,如100s, 而且max-age优先级更高。
max-age以及expires头指定了下次更新缓存的时间,而如何获取资源则是由Etag和Last-Modified决定。
条件请求
当浏览器缓存过期时,就会发送请求来更新缓存。条件请求会先咨询服务器本地缓存内容是否已经变更,浏览器会发送缓存的一些信息给服务器,用来确定缓存对应的服务器资源。如果缓存资源更新了则发送最新内容,否则返回304,浏览器继续使用缓存。
基于时间的条件请求
如果自资源被缓存之日起,缓存内容未改变,则服务器直接返回304.开启时间条件缓存需要服务器返回Last-Modified 响应头,指定被缓存时的最后修改时间。
Cache-Control:public, max-age=31536000
Last-Modified: Mon, 03 Jan 2011 17:45:57 GMT
下一次,浏览器发送更新缓存的请求时,会使用If-Modified-Since头来咨询缓存对应的资源是否在这个时间以后变更过。
If-Modified-Since: Mon, 03 Jan 2011 17:45:57 GMT
如果资源在Mon, 03 Jan 2011 17:45:57 GMT 之后未改变,则服务器返回304,浏览器继续使用缓存。
基于内容的条件请求
该类型的条件请求跟时间条件请求类似,只不过用的是资源的检验码(checksum),比如文件的md5 hash。当无法确定最近修改时间时,Etag是对基于时间的条件请求很好的补充。 返回头如下:
Cache-Control:public, max-age=31536000 ETag: "15f0fff99ed5aae4edffdd6496d7131f"
当浏览器发送更新资源的请求时,将会带上If-None-Match请求头。
If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"
如果服务器资源的hash码以此一致,则返回304.
图例
禁止缓存
因为IE使用的是no-cache来禁止缓存,所以返回头如下:
Cache-Control:no-cache, no-store
使用nginx添加缓存控制
location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
}
参考:
https://varvy.com/pagespeed/cache-control.html
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec1
https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers