在浏览器第一次请求某一个URL时9159.com:

作者: 前端  发布:2019-11-21

HTTP 缓存

2016/11/01 · 基础技术 · HTTP, 缓存

原文出处: 凹凸实验室   

9159.com 1

有时,HTTP 中的缓存可能会非常让人头疼。
按照文档正确地使用 HTTP 并不是那么困难,但事实上,不同的浏览器和 HTTP 版本常常困扰着我们。

通过 Stack Overflow 的搜索结果,你可以很轻易地发现很多人有相同的困扰。我们自己或是不必或是没有时间去钻研所有的边缘的情况。

所以这里有一些博主总结的实用并速记的规则,并且在未来博主也会持续地跟进和更新。

浏览器缓存控制,浏览器缓存

浏览器缓存控制

Last-Modified/ If-Modified-Since

在浏览器第一次请求某一个URL时,服务器端的返回状态码200,内容是客户端请求的资源,同时有一个Last-Modified的属性标记此文件在服务器端最后被修改的时间。

Last-Modified格式类似这样:Last-Modified : Fri , 12 May 2006 18:53:33 GMT

客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头,询问该时间之后文件是否有被修改过:

If-Modified-Since : Fri , 12 May 2006 18:53:33 GMT

如果服务器端的资源没有变化,则自动返回 HTTP 304(Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

Last-Modified的问题

1、一些文件会被编辑,但内容并未改变,这个时候不希望客户端认为这个文件被修改了,而重新获取资源。

2、某些文件修改非常频繁,比如在秒以下的时间内进行修改,If-Modified-Since无法检查到。

3、某些服务器不能精确的得到文件的最后修改时间。

 

Expires

Expires用来控制缓存失效的日期。当浏览器看到响应中有一个Expires头时,它会和相应的组件一起保存到其缓存中,只要组件没有过期,浏览器就会使用缓存版本而不会进行任何的HTTP请求。长久的Expires头适用于图片等不经常更新的资源。Expires设置的日期格式必须为GMT(格林尼治标准时间)。

Expires:Wed, 11 Jan 2017 08:10:26 GMT

Expires的不足:

首先,Expires头使用的是一个特定的时间,要求客户端和服务器端的时钟严格同步。如果服务器和客户端的时间不统一,这有可能出现缓存提前失效的情况,存在不稳定性。其次,假如Expires的日期到来,需要在服务器配置中再提供一个新的日期。

 

Cache-Control

HTTP1.1引入了Cache-Control头来克服Expires头的不足。Cache-Control使用max-age制定组件被缓存多久,以秒为单位。例如

Cache-Control:max-age=3600表示组件将被缓存60分钟。

如果max-age和Expires同时出现,则max-age有更高的优先级,浏览器会根据max-age的时间来确认缓存过期时间。

常用 cache-directive 值

Cache-directive

说明

public

所有内容都将被缓存(客户端和代理服务器都可缓存)

private

9159.com,内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存),默认值

no-cache

必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。

no-store

所有内容都不会被缓存到缓存或 Internet 临时文件中

must-revalidation/proxy-revalidation

如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证

max-age=xxx (xxx is numeric)

缓存的内容将在 xxx 秒后失效, 这个选项只在HTTP 1.1可用。如果和Last-Modified一起使用时, 优先级较高

 

浏览器的不同操作

打开新窗口

如果指定cache-control的值为private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。而如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器,例如:Cache-control: max-age=5 表示当访问此网页后的5秒内不会去再次访问服务器.

在地址栏回车

如果值为private或must-revalidate,则只有第一次访问时会访问服务器,以后就不再访问。如果值为no-cache,那么每次都会访问。如果值为max-age,则在过期之前不会重复访问。

按后退按扭

如果值为private、must-revalidate、max-age,则不会重访问,而如果为no-cache,则每次都重复访问.

按刷新按扭

无论为何值,都会重复访问.

 

Etag(Entity Tag)

服务器在检测缓存的组件是否和原始服务器上的组件匹配时有两种方法:

(1)比较最近修改日期;Last-Modified/If-Modified-since

(1)比较实体标签;Etag/If-None-Match(优先级比If-Modified-since高)

实体标签,是web服务器和浏览器用于确认缓存组件的有效性的一种机制。

           ETag:"50b1c1d4f775c61:df3"

第一次请求时:

1.客户端发起HTTP GET 请求一个资源;

2.服务器处理请求,返回资源,包括Http Etag和状态码200

第二次请求时:

1.客户端发起 HTTP GET 请求一个文件,请求中包括一个If-None-Match头,内容就是第一次请求时服务器返回的Etag的值

2.服务器判断接受到的Etag和计算出来的Etag是否匹配。若匹配,返回304状态码,客户端继续使用本地的缓存。若不匹配,返回资源和新的ETag。

ETag带来的问题

ETag通常使用组件的某些属性来构造它,这些属性对于特定的寄宿了网站服务器来说是唯一的。当浏览器从一台服务器上获取了原始组件,之后又向另外一台不同的服务器发起条件GET请求时,ETag是不会匹配的。

对组件进行不必要的重新加载还会影响服务器的性能并增加带宽开销。如果你的RoundRobin Rotation集群中有n台服务器,下一次用户缓存中的Etag能和服务器匹配的概率是1/n。Etag还会降低代理缓存的效率,因为代理的与自身的不匹配会重新下载。

HTTP响应优化

移除ETag,Expires,使用Cache-Control控制本地缓存。

浏览器缓存控制 Last-Modified/ If-Modified-Since 在浏览器第一次请求某一个URL时,服务器端的返回状态码200,内容...

静态资源

永远不会修改的内容:JS 和 CSS 文件,图片,和任何类型的二进制文件都属于这个类目。

永远,我确实说的是永远。为静态资源指定版本号是很通用的做法。它们无论什么时候改动了,它们的 URL 就改变了。

这里是一些针对静态资源的简单的规则:

  • 在文件或者路径中嵌入指纹。避免为指纹使用查询字符串。另外,确保生成的URL长度超过8个不同的字符。
  • 使用这些 HTTP 头:
Cache-Control: public, max-age=31536000 Expires: (一年后的今天)
ETag: (基于内容生成) Last-Modified: (过去某个时间) Vary:
Accept-Encoding

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6a212f047787520650-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6a212f047787520650-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6a212f047787520650-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6a212f047787520650-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6a212f047787520650-5">
5
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6a212f047787520650-1" class="crayon-line">
Cache-Control: public, max-age=31536000
</div>
<div id="crayon-5b8f6a212f047787520650-2" class="crayon-line crayon-striped-line">
Expires: (一年后的今天)
</div>
<div id="crayon-5b8f6a212f047787520650-3" class="crayon-line">
ETag: (基于内容生成)
</div>
<div id="crayon-5b8f6a212f047787520650-4" class="crayon-line crayon-striped-line">
Last-Modified: (过去某个时间)
</div>
<div id="crayon-5b8f6a212f047787520650-5" class="crayon-line">
Vary: Accept-Encoding
</div>
</div></td>
</tr>
</tbody>
</table>

针对静态资源的设置就是那么简单。

动态资源

针对应用程序私密性和新鲜度方面需求的不同,我们应该使用不同的缓存控制设置。

对于非私密性和经常性变动的资源(想像一下股票信息),我们应该使用下面这些:

Cache-Control: public, max-age=0 Expires: (当前时间) ETag: (基于内容生成) Last-Modified: (过去某个时间) Vary: Accept-Encoding

1
2
3
4
5
Cache-Control: public, max-age=0
Expires: (当前时间)
ETag: (基于内容生成)
Last-Modified: (过去某个时间)
Vary: Accept-Encoding

这些设置的效果是:这些资源可以被公开地(通过浏览器和代理服务器)缓存起来。每一次在浏览器使用这些资源之前,浏览器或者代理服务器会检查这些资源是否有更新的版本,如果有,就把它们下载下来。

这样的设置需要注意,浏览器在重新检查资源时效性方面有一定的灵活性。典型的是,当用户点击了「返回/前进」按钮时,浏览器不会重新检查这些资源文件,而是直接使用缓存的版本。你如果需要更严格的控制,需要告知浏览器即使当用户点击了「返回/前进」按钮,也需要重新检查这些资源文件,那么可以使用:

Cache-Control: public, no-cache, no-store

1
Cache-Control: public, no-cache, no-store

不是所有的动态资源都会马上变成过时的资源。如果它们可以保持至少5分钟的时效,可以使用:

Cache-Control: public, max-age=300

1
Cache-Control: public, max-age=300

经过这样的设置,浏览器只会在5分钟之后才重新检查。在这之前,缓存的内容会被直接使用。如果在5分钟后,这些过时的内容需要严格控制,你可以添加 must-revalidate 字段:

Cache-Control: public, max-age=300, must-revalidate

1
Cache-Control: public, max-age=300, must-revalidate

对于私密或者针对用户的内容,需要把 public 替换为 private 以避免内容被代理缓存。

Cache-Control: private, …

1
Cache-Control: private, …

Cache-Control 和 Expires

当同时使用 Cache-ControlExpires 时,Cache-Control 获得优先权。

同时使用 Cache-ControlExpires 意味着得到更广泛的支持(被不同的浏览器和版本)。当然,它们两个应该被配置成相同的时效值,以避免引起困惑。

参考 Expires: vs. Cache-Control: max-age

ETag 和 Last-Modified

这两个头在浏览器对资源做重新检查验证的时候会使用到。大致来说,浏览器只是盲目地存储这两个来自于服务器的头的值,然后在需要检查验证的时候,浏览器根据请求条件,把这两个指发送给服务器(分别通过 If-None-MatchIf-Modified-Since)。

注意只有在资源过期的情况下,检查验证才会发生。

在有条件的请求下,If-None-MatchIf-Modified-Since 头的出现取决于服务器。然而,由于是服务器生成的 ETag 和(或) Last-Modified,所以实际上,这没有什么大问题。大多数的浏览器在可能的情况下都会把着两者都发送给服务器。

参考 What takes precedence: the ETag or Last-Modified HTTP header?

一个通常的建议是:避免使用 ETag。这不是一个总是有用的建议。ETag 在判断内容是否真的改动方面确实提供了更为精确的控制。针对生成的 ETag,默认的Apache方法需要把文件的索引节(inode),大小(size)和最后修改时间作为输入求值得到。这会导致在负载均衡的环境中,生成的 ETag 值变得毫无用处,因为每个服务器都会针对相同的文件生成一个不同的 Etag 值。这个可能就是唯一的问题导致很多人完全禁用 ETag,其实只要精确地针对一个匹配的文件生成一个独一无二的 ETag 值,就没有必要禁用 ETag 了。

参考 Should your site be using etags or not?

手动按下 Ctrl-R

当按下 Ctrl-R 时,浏览器会携带下面的请求,以检查是否需要更新缓存内容:

Cache-Control: max-age=0 If-None-Match: … If-Modifed-Since: …

1
2
3
Cache-Control: max-age=0
If-None-Match: …
If-Modifed-Since: …

注意这并不只是和原服务器建立连接,其同样适用于代理服务器。本质上,它只是重新检查验证内容。如果服务器回应了一个304,浏览器将会使用缓存的内容。

Vary: Accept-Encoding

这个头对于一些人来说可能比较陌生。

当一个资源启用了 gzip 压缩,并且被代理服务器缓存,客户端如果不支持 gzip 压缩,那么在这样的情况下将会得到不正确的数据(也就是,压缩过的数据)。这将会使代理服务器缓存两个版本的资源:一个是压缩过的,一个是没压缩过的。正确版本的资源将在请求头发送之后进行传输。

还有一个现实的原因:IE 浏览器不缓存任何带有 Vary 头但值不为 Accept-EncodingUser-Agent 的资源。所以通过这种方式添加这个头,才能确保这些资源在 IE 下被缓存。

本文译自 Bryan Tsai 的 《Http Caching》。

参考资料

  • Increasing Application Performance with HTTP Cache Headers
  • Things Caches Do
  • Google Developers: HTTP Caching
  • Google Developers: Optimize Caching
  • Caching Tutorial for Web Authors and Webmasters
  • Cache Control Directives Demystified
  • What are the hard and fast rules for Cache Control?
  • What’s the difference between Cache-Control: max-age=0 and no-cache?
  • HTTP Cache Control max-age, must-revalidate
  • Difference between no-cache and must-revalidate
  • Caching Improvements in Internet Explorer 9

    2 赞 6 收藏 评论

9159.com 2

本文由9159.com发布于前端,转载请注明出处:在浏览器第一次请求某一个URL时9159.com:

关键词:

上一篇:没有了
下一篇:没有了