https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers
现在的时代,程序员可以有很多方法来提高程序性能和体验,而经常被疏忽遗忘的就是通过 HTTP 缓存来实现.
http 缓存是一个能够到绝大多数浏览器上都适用的技术,在现在的 web 应用里容易实现.合理的使用能够很大得提高的你程序的性能, 提高响应时间或者降低服务器的压力,但是不正确的使用也会造成一些问题, 内容过期不更新啊,很难 debug 问题啊. 下面我们看看到底怎么使用 http 缓存.
概述
当浏览器把 web 上的资源保存到本地的时候就会发生 http 缓存.然后在下一次就能够很快的响应这个资源的请求. 你的程序可以给特定的资源响应头上添加一个行为来达到缓存的目的.
当一个 item 被完整的缓存起来后,浏览器就不会再去访问服务器,只是使用本地的缓存.
举个例子, 一旦 CSS 文件从你的程序哪里下载后,浏览器就不会再去下载了.对于当前访问的用户来说.当然这个对于很多其他的资源文件也是起作用的.如文件,图片,甚至不经常更新的内容.在这些情况中, 它非常有用如果把内容缓存到本地的话.
HTTP 缓存头
有2个比较主要的缓存头: Cache-Control
和 Expires
.
Cache-Control
没有 cahe-control 这个头的话,就其他的也不好使了
这个值通常是非常重要的设置,它可以作为一个开关的形式告诉浏览器是否缓存. 设置了这个值之后, 就能够启用缓存, 浏览器就会缓存文件按照你指定的时间长度. 如果没有这个值,就会总是去请求服务器.
public
资源可以不仅仅缓存到终端用户的浏览器中,而且还可以缓存到任何的中间代理那里,这样它就可以作用于其他的用户.
Cache-Control:public
private
资源会被绕过中间的代理,只能够缓存到终端用户.
Cache-Control:private
Cache-Control
这个头是一个组合的值,表明是否资源是公有还有私有的, 而且还要指定最大有效时间,在过期之前.这个 max-age
值设置为时间来说明要缓存多久对于这个资源, 弄秒记的.
Cache-Control:public, max-age=31536000
当这个 cache-control
头开启并且设置了 max-age
, 这个 expires
头是被用来指定在一个特定时间点,告诉浏览器这个过期了.
expires
当和 cache-control
一起作用的时候, expires
简单的给了一个日期来设置在什么时间就会过期. 之后就会去服务器取最新的,然后就再等下一次的过期.
如果同时设置了
expires
和max-age
,max-age
会优先作用.
Cache-Control:public
Expires: Mon, 25 Jun 2012 21:31:12 GMT
条件性请求
条件性请求缓存的实现就是浏览器会询问服务器当前缓存的文件在他这里是否有更新的,如果有就会请求最新的. 在询问的时候会携带一些信息,这些信息能够让服务器判断是否有最新的信息.没有的话就返回304
了.
虽然这样的方式会有请求到服务器,但是如果没有更新返回304
是情况下,响应的内容是空的,可以节省返回时的带宽,这样就会节省响应时间.
基于时间的
基于时间的条件性请求就是确保如果请求的资源已经修改了在浏览器这里缓存的情况下,它会被更新,如果浏览器手里是最新的,它就不会被请求.
实现这个缓存的方式就是通过设置 last-modified
资源的最后跟踪时间来完成.
Cache-Control:public, max-age=31536000
Last-Modified: Mon, 03 Jan 2011 17:45:57 GMT
下一次浏览器请求这个资源的时候,如果他们在这个last-modified-since
的值之后更新过的话,它会询问当前的资源的最新内容.
基于内容的
基于内容的就是会通过 etag
这个值来判断,他和 last-modified
头有点类似,除了他的值是一个内容的摘要.举个例子可以是 MD5值什么的. 这个允许服务器来鉴定当前缓存的资源内容是否和服务器这边的一样.
这个会在使用
last-modified
比较困难的情况下使用.
Cache-Control:public, max-age=31536000
ETag: "15f0fff99ed5aae4edffdd6496d7131f"
之后, 浏览器会请求这个 if-none-math
请求头会和 etag
,
If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"
和if-modified-since
头一样,如果当前版本有一样的 etag 值,那就返回一个304.
可见性
现在的浏览器都有开发者工具,可以查看请求和响应内容,如下图,你可以看到一个初始化的请求到这个地址的. 这个时候你发现没有任何的 cache 指令.
通过添加查询参数,http://http-caching-demo.herokuapp.com/?cache=true,
应用程序开启了缓存,使用了 cache-control
和 expires
头,两个都设置了30秒.
添加一个 etag
参数到请求, http://http-caching-demo.herokuapp.com/?etag=true, 就会在头里添加一个 etag 的值,
继续深究,会发现基于 etag 的条件性请求预期执行了,通过一个初始化的请求从浏览器发送到服务器.
但是接下来的请求就会得到一个304 的响应.
使用情景
静态资源
Cache-Control:public; max-age=31536000
Expires: Mon, 25 Jun 2013 21:31:12 GMT
这个基本上一般都是缓存的
动态内容
私有内容
Cache-Control:private, max-age=31536000
禁止缓存
Cache-Control:no-cache, no-store
各语言的实现
略