http缓存机制在使用nginx设定静态资源是否被缓存时就会被用到,当初做项目时就遇到这个触及我知识盲区的问题。所以现在整理下。
像nginx、cdn等服务器端缓存具体用到时具体分析。这里主要了解浏览器端缓存!
整理自 孙世吉的http缓存控制小结
浏览器HTTP缓存详解
HTTP首部缓存控制字段
HTTP 1.0
- expires:Mon, 22 Jul 2002 11:12:01 GMT ,固定GMT(格林尼治时间),但局限于每个地方的时区不同,所以在http1.1中一般不用。
- pragma:no-cache 在http1.0中表示不缓存,每次请求都访问服务器。
HTTP 1.1:
- 针对上述expires无法统一客户端时间问题,http1.1推出了cache-control,其设置的是相对时间,精确到秒,过了这段时间即表示资源过期。但为了向下兼容,还是保留expires。优先级为 pragam>cache-control>expires。
cache-control存在以下值:
请求报文中:
字段 | 含义 |
---|---|
no-cache | 告知(代理)服务器不使用缓存,直接向原服务器请求 |
no-store | 所有内容都不会被保存到浏览器缓存或internet临时文件中! |
max-age=delta-seconds | 告知服务器,客户端希望接收一个存在时间不超过delta-seconds的资源 |
max-stale[=delta-sonds] | 指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。 |
no-transform | 告诉代理服务器客户端希望获取没有被转换(比如压缩)的数据 |
only-if-cached | 告知(代理)服务器客户端希望获取缓存,而不用想原服务器获取(若代理服务器有) |
cache-extension | 自定义扩展值,服务器无法识别则忽略! |
响应报文中:
字段 | 含义 |
---|---|
public | 则即使它有关联的 HTTP 身份验证,甚至响应状态代码通常无法缓存,也可以缓存响应。 大多数情况下,“public”不是必需的,因为明确的缓存信息(例如“max-age”)已表示响应是可以缓存的。 |
private[=“field-name”] | 这些响应通常只为单个用户缓存,因此不允许任何中间缓存对其进行缓存。 例如,用户的浏览器可以缓存包含用户私人信息的 HTML 网页,但 CDN 却不能缓存。 |
no-cache | 必须先与服务器确认返回的响应是否发生了变化,然后才能使用该响应来满足后续对同一网址的请求。 因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,但如果资源未发生变化,则可避免下载。 |
no-store | 它直接禁止浏览器以及所有中间缓存存储任何版本的返回响应,例如,包含个人隐私数据或银行业务数据的响应。 每次用户请求该资产时,都会向服务器发送请求,并下载完整的响应。 |
no-transform | 客户端缓存时不得转换数据 |
max-age=delta-seconds | 指令指定从请求的时间开始,允许提取的响应被重用的最长时间(单位:秒)。 例如,“max-age=60”表示可在接下来的 60 秒缓存和重用响应。 |
must-revalidate | 略 |
proxy-revalidate | 略 |
s-max-age=delta-seconds | 略 |
Cache-Control 允许自由组合可选值,例如:
Cache-Control: max-age=3600, must-revalidate

数据校验字段
缓存在客户端的数据有了控制过期的时间,当过期了或者是有相关设置每次一定要访问服务器检验有效期,都会连接服务器访问服务器,检验是否真的需要更新本地缓存,觉得返回200还是304。原文一个形象的例子:
C:小服,你几岁了?
S:小客,我18岁了。(200)
=================================
C:小服 ,你几岁了?我猜你18岁了。
S:靠,你知道还问我?(服务器检验,未过期,返回304)
=================================
C:小服 ,你几岁了?我猜你18岁了。
S:小客 ,我19岁了。(服务器检验已过期,返回数据与状态码200)
1. Last-Modified
服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码,内容为空,这样就节省了传输数据量 。如果两个时间不一致,则服务器会发回该资源并返回200状态码,和第一次请求时类似。这样保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。一个304响应比一个静态资源通常小得多,这样就节省了网络带宽。
If-Modified-Since
和Last-Modified对应,服务器返回的资源最后修改时间在请求检验时会被放在这个字段值中,用来和服务器资源最后修改时间对比!

If-Unmodified-Since
该值告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412(Precondition Failed) 状态码给客户端。 Last-Modified 存在一定问题,如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为Last-Modified时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。
2. ETag
前面的cache-control时间能精确到秒,但是如果资源的改动在秒以内,检验就存在不确定性了。为了解决上述Last-Modified可能存在的不准确的问题,Http1.1还推出了 ETag 实体首部字段。
其本质是资源子服务器中各种信息通过某一种算法生成的一种唯一标识。如Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
Etag: "5d8c72a5edda8d6a:3239"
同理还有两个检验信息
If-None-Match
示例为 If-None-Match: "5d8c72a5edda8d6a:3239" 告诉服务端如果 ETag 没匹配上需要重发资源数据,否则直接回送304 和响应报头即可。 当前各浏览器均是使用的该请求首部来向服务器传递保存的 ETag 值。
If-Match
告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412(Precondition Failed) 状态码给客户端。否则服务器直接忽略该字段。
需要注意的是,如果资源是走分布式服务器(比如CDN)存储的情况,需要这些服务器上计算ETag唯一值的算法保持一致,才不会导致明明同一个文件,在服务器A和服务器B上生成的ETag却不一样。
网友评论