• Ukieweb

    佳的博客

    曾梦想仗剑天涯,后来工作忙没去。

使用 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 缓存之外,Web 性能优化还有很多其他途径,比如 预加载和预渲染脚本异步载入 等。

HTTP 缓存简介

谈起 HTTP 缓存你首先想到的一定是磁盘缓存(from disk cache),以及 304 状态码。 这是浏览器处理缓存的两种情况:

  • 资源仍然处于有效期时,浏览器会直接使用磁盘缓存(在刷新时稍有不同,见下文)。

  • 缓存过期后,浏览器询问服务器缓存是否有效,服务器返回 304 指示浏览器使用缓存。

httpcache1.jpg

图中 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 标识。

httpcache2.jpg

除了 Cache-Control 中的max-age 外,ExpiresVary 等头字段也可用来设置缓存的有效性。

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 字段。

httpcache3.jpg

  • 已知 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, 见下图:

httpcache4.jpg

Last-Modified 有两个问题

  1. 只能精确到 ,1 秒内的多次变化反映不出来;

  2. 在轮询的负载均衡算法中,如果各机器读到的文件修改时间不一致,有缓存无故失效和缓存不更新的风险

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
0
下一篇:HTTP缓存参数 中 Etag 的重要性

0 条评论

老佳啊

85后,大专学历,中原人士,家里没矿。

由于年轻时长的比较帅气,导致在别人眼里,我一直不谈恋爱的原因是清高,实则是自己的小自卑。最大的人生目标就是找一个相知相爱相容的人,共度余生。

和人相处时如果能感受到真诚,会非常注重彼此的关系,对别人没有什么心机,即使有利益冲突,一般也会以和为贵,因为在这个世界上,物质的东西,从来不会吸引到我。

特别迷恋那些大山大水,如果现在还能隐居,可能早就去了。对那些宏伟的有底蕴的人文景观比较不感冒。

从事于IT行业,却一直对厨房念念不忘,由于身材魁梧,总觉得自己上辈子是个将军,可惜这辈子没当兵,也不会打架。