当 Nginx 使用 proxy cache 的文件做为响应时,它会更新其中的一些内容,好比 Date 响应头;但大部分响应头都不会获得更新,好比 Expires 和 Cache-Control。众所周知,Cache-Control 能够经过 max-age=xxx 或者 s-maxage=xxx 指令设置缓存的有效时间。跟 Expires 响应头不一样,这一时间是相对的。假设上游服务器返回 Cache-Control: public; max-age=3600
,那么 Nginx 会缓存该响应一小时。若是在这一小时到期以前,Client 访问了 Nginx,它会获取到一样的 Cache-Control 响应头,所以会再缓存多一小时。因此整体上该响应会被缓存两小时。web
这听起来很让人惊讶。但仔细想一想,其实也不算什么严重的问题。首先,当咱们设置 max-age=3600
时,大多数状况下并不要求其严格地在一小时后过时。其次,这个算是通常的多层缓存固有的弊端:缓存数据的最大过时时间,取决于各级缓存 TTL 的总和。若是想要避免,你能够选择根据外层数据剩下的 TTL 设置当前 TTL;或者提供主动 purge 的操做,从最内层开始逐层清理数据。浏览器
固然,某些时候下,这一行为会带来一些问题。举个例子,假设咱们开启了 proxy_cache_use_stale,在上游服务器出问题时使用过时的内容代替正常的响应。这种状况下,缓存只是做为一个临时救急的方案使用,咱们并不但愿 Client 多缓存更多的时间。不然会有上游应用的开发者抱怨,为什么上游服务器已经正常了,用户刷新页面看到的仍是旧数据。做为解决办法,咱们能够在 Nginx 的 header filter 阶段,经过 Lua 代码或者 Nginx C module,把 Cache-Control: max-age=...
修改为 Cache-Control: no-cache
。这么一来,Client 会在使用缓存以前先验证下,若是 Nginx 返回 304 状态码,那么该缓存会被继续使用;若是上游已经 OK 了且更新了响应,那么 Client 就会从新请求,避免使用过时的内容。缓存
这里须要强调下,no-cache
并不是如字面上的意义表示不缓存,而是要求 Client 在使用该缓存以前,须要先验证下被缓存的内容是否仍是最新的。MDN 的说法是:服务器
Forces caches to submit the request to the origin server for validation before releasing a cached copy.
对应的,RFC 7234 的说法:code
The "no-cache" request directive indicates that a cache MUST NOT use
a stored response to satisfy the request without successful
validation on the origin server.
若是要想让 Client 不缓存响应的内容,按 MDN 上的说法,须要用 Cache-Control: no-cache, no-store, must-revalidate
(https://developer.mozilla.org...)。server
仔细看了下 no-cache
/ no-store
/ must-revalidate
这三项指令的介绍,彷佛 no-store
就能让 Client 不用这个缓存,由于 no-store
要求:开发
The cache should not store anything about the client request or server response.
另外 must-revalidate
要求在使用过时缓存前验证下该内容是不是最新的,而 no-cache
也是要求从新验证的,那为何须要两个都一块儿用呢?get
Google 搜索把我带到了这个 SO 问答:https://stackoverflow.com/que...。这个回答里面解释了为什么不仅仅用 no-store
:由于臭名昭著的 IE6 浏览器在处理 no-store
时有 bug。但惋惜的是,这个回答没有给出这一论断的证据,好比 IE 的 bug report 之类。MDN 在给出 Cache-Control: no-cache, no-store, must-revalidate
这个例子的时候,也没有说起更多的上下文。这很像没有任何注释的老代码:咱们不知道当初为什么这么写,而把它删掉彷佛不会带来什么问题。it