使用 HTTP 缓存:Etag, Last-Modified 与 Cache-Control
整个 Web 系统架构在 HTTP 协议 之上, 利用 HTTP 的缓存机制不仅可以极大地减少服务器负载, 更重要的是加速页面的载入,以及减少用户的流量消耗。 快速到达和易于访问是 Web 与生俱来的特性, 其缓存机制也早已被服务器和浏览器厂商广泛地实现, 我们作为 Web 内容的作者何乐而不为呢?
Web 服务器(比如 Tomcat、Apache、Virgo)或服务器端框架(比如 Django、Express.js) 都会实现 HTTP 缓存机制,但本文不借助这些框架, 而是直接以基本的 Node.js 程序与 Chrome 浏览器来描述 HTTP 中最基本的缓存机制, 涉及到的 HTTP 头字段 包括 Cache-Control, Last-Modified, If-Modified-Since, Etag, If-None-Match 等。
HTTP 缓存简介
谈起 HTTP 缓存你首先想到的一定是磁盘缓存(from disk cache),以及 304 状态码。 这是浏览器处理缓存的两种情况:
资源仍然处于有效期时,浏览器会直接使用磁盘缓存(在刷新时稍有不同,见下文)。
缓存过期后,浏览器询问服务器缓存是否有效,服务器返回 304 指示浏览器使用缓存。
图中 favicon.ico 直接来自磁盘缓存,而 localhost 文档则来自 304 缓存。 上述行为中涉及到 3 个 HTTP 响应头字段:
Cache-Control 响应头表示了资源是否可以被缓存,以及缓存的有效期。
Etag 响应头标识了资源的版本,此后如果缓存过期,浏览器可据etag的值询问服务器如果服务器未更新那么etag值一样再进行缓存
Last-Modified 响应头标识了资源的修改时间,此后浏览器可据此进行缓存以及询问服务器。
Cache-Control
Cache-Control 在 HTTP 响应头中,用于指示代理和 UA 使用何种缓存策略。比如:
no-cache 为本次响应不可直接用于后续请求(在没有向服务器进行校验的情况下)
no-store 为禁止缓存(不得存储到非易失性介质,如果有的话尽量移除,用于敏感信息)
private 为仅 UA 可缓存
public 为大家都可以缓存。
当 Cache-Control 为可缓存时,同时可指定缓存时间(比如 public, max-age:86400)。 这意味着在 1 天(60x60x24=86400)时间内,浏览器都可以直接使用该缓存(此时服务器收不到任何请求)。 当然浏览器也有权随时丢弃任何一项缓存,因此这里可能有一致性问题。 注意下图中状态码附近的 from disk cache 标识。
除了 Cache-Control 中的max-age 外,Expires,Vary 等头字段也可用来设置缓存的有效性。
Etag (if-None-Match)
https://tools.ietf.org/html/rfc7232#section-2.3
如果资源本身确实会随时发生改动,还用 Cache-Control 就会使用户看到的页面得不到更新。 但如果还希望利用 HTTP 缓存(万一资源没变呢),这就需要有条件的(conditional)HTTP 请求。
Etag 响应头字段表示资源的版本,浏览器在发送请求时会带 If-None-Match 头字段, 来询问服务器该版本是否仍然可用。如果服务器发现该版本仍然是最新的, 就可以返回 304 状态码指示 UA 继续使用缓存。注意下图中的 If-None-Match 字段。
已知 nginx 1.7.3 以下的版本会在 gzip 的过程中 remove 掉 etag。
如果nginx 开启了 ssi 模块 ,默认会抹掉 ETag 和 Last-Modified 头,需要增加参数: ssi_last_modified on;
Last-Modified
与 Etag 类似,Last-Modified HTTP 响应头也用来标识资源的有效性。 不同的是使用 修改时间 而不是实体标签。对应的请求头字段为 If-Modified-Since, 见下图:
Last-Modified 有两个问题
只能精确到 秒,1 秒内的多次变化反映不出来;
在轮询的负载均衡算法中,如果各机器读到的文件修改时间不一致,有缓存无故失效和缓存不更新的风险
Chrome 浏览器 的两种刷新
正常重新加载
按下刷新按钮(F5)会触发浏览器的“正常重新加载”(normal reload), 此时浏览器会执行一次 Conditional GET。 Cache-Control 等缓存头字段会被忽略,并且带If-None-Match, If-Modified-Since等头字段。 此时服务器总会收到一次 HTTP GET 请求。 在 Chrome 中按下刷新,浏览器还会带如下请求头:
Cache-Control:max-age = 0
注意:在地址栏重新输入当前页面地址并按下回车也会当做刷新处理, 这意味着只有从新标签页或超链接打开时,才能观察到直接使用硬盘缓存的情况。
强制重新加载
在 Chrome 中按下(CTRL+F5)可以触发强制重新加载(Hard Reload), 此时包括页面本身在内的所有资源都不会使用缓存。 浏览器直接发送 HTTP 请求且不带任何条件请求字段。
转自:https://harttle.land/2017/04/04/using-http-cache.html
共 0 条评论