IE下GET请求缓存问题及解决方案

什么是浏览器缓存,为什么要使用它?

缓存游走于服务器和客户端之间。服务器可能是源服务器(存放请求资源的服务器),也可能是反向代理服务器(如 Nginx),数量为 1 个或多个;客户端可能为 PC 端、移动端等,数量也为 1 个或多个。简单的说,缓存用于监控服务器与客户端之间的通信,监控客户端请求,并且把请求输出的内容(如页面、 图片和文件)拷贝一份副本存在本地;然后,如果下一个请求是相同的 URL,则直接请求保存的副本,而不用再次向服务器获取。

使用缓存的 3 个主要原因:

  • 提升访问速度:缓存离客户端更近,因此,从缓存请求数据比从服务器所用时间更少,显著地提高了网站的访问速度。
  • 减少网络带宽消耗:副本被重复使用,降低请求次数,从而减少网络流量,节省运营成本。
  • 降低服务器压力:给网络资源设定有效期之后,用户可以重复使用本地的缓存,减少对源服务器的请求,间接降低服务器的压力

浏览器缓存规则

对于浏览器端的缓存来讲,这些规则是在 HTTP 协议头和 HTML 页面的 Meta 标签中定义的,分别新鲜度校验值两个维度来确定浏览器是以直接使用缓存中的副本,还是需要去源服务器获取更新的版本。

  • 新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下条件,浏览器会认为它是有效的:

    (1) 含有完整的过期时间控制头信息(HTTP 协议报头),并且仍在有效期内;
    (2) 浏览器已经使用过这个缓存副本,并且在首次请求中已经检查过有效性(非首次请求);

    满足以上两个情况的一种,浏览器会直接从缓存中获取副本并渲染。

  • 校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签 Etag(Entity Tag),它可以用来作为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过期,服务器需重新返回资源内容。

浏览器请求流程图如下:

cache

请求本地缓存过后,浏览器会向服务端发起请求,协商是否使用缓存,如果上一次的缓存中有 Last-modified 和 Etag 字段,浏览器将在 request header 中加入 If-Modified-Since(对应于 Last-modified), 和 If-None-Match(对应于 Etag)。

两个概念:

  • 强缓存:用户发送的请求,直接从客户端缓存中获取,不发送请求到服务器,不与服务器发生交互行为
  • 协商缓存:用户发送的请求,发送到服务器后,由服务器判定是否从缓存中获取资源

    两者共同点是客户端获得的数据最后都是从客户端缓存中获得。区别在于强缓存不与服务器交互,而协商缓存则需要与服务器交互。

IE 浏览器下的缓存问题

IE 浏览器会缓存网页中的 GET 和 XHR 的内容,并且在 IE 浏览器中如果请求方式是 GET 的话,IE 浏览器会对其进行标识,如果该 GET 请求的 URL 是第一次请求的话,会请求相关服务器,从数据库中获取数据;而如果该 GETt 请求的 URL 不是第一次请求的话,IE 浏览器会直接从缓存副本中拿到上次该 URL 获取的数据。并且无论是形式封装的 GET 方式请求,IE 浏览器都会这样进行处理的。这对于实时性有要求的数据不适用,产生网页中的数据与数据库的数据不同步的现象。

几种解决上述问题的方法

(1) GET 参数中添加一个随机数或当前时间戳的参数

保证每次 URL 请求唯一性,迫使每个 GET 请求都是一次新请求,这种方式虽然能解决 IE 始终返回 304 的问题,但实际上每个 request 都会去请求服务器,对 web 优化并非最佳的解决方案。

(2) 请求方式修改为 post 方法

这种做法不符合 RESTful web API 设计,同时这种方式也会每次请求服务器,没有真正利用到 IE 的缓存机制。

(3) 在 HTML 页面设置 Meta 标签

1
2
<meta http-equiv="Cache-Control" content="no-store" />
<meta http-equiv="Pragma" content="no-cache" />

这种禁用缓存的形式用处很有限:仅有 IE 才能识别这段 meta 标签含义,其它主流浏览器仅识别“Cache-Control: no-store”的 meta 标签;在 IE 中识别到该 meta 标签含义,只能保证请求当前 HTML 页面时每次都发新请求,网页上的资源则不受影响;因浏览器不同或者同一浏览器间版本不同,此方法存在很大的兼容性问题。

(4) 使用缓存有关的 HTTP 消息报头控制(协商缓存)

  • Cache-Control 与 Expires

    Cache-Control 与 Expires 的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。相比较 Cache-Control 的选择更多,设置更细致,如同时设置,其优先级高于 Expires。

  • Last-Modified/ETag 与 Cache-Control/Expires

    配置 Last-Modified/ETag 的情况下,浏览器再次访问该 URI 的资源,仍会发送请求到服务器询问文件是否已经修改。如未修改,服务器会只发送一个 304 回给浏览器,告诉浏览器直接从本地的缓存取数据;如已修改就返回修改过的数据。

    Cache-Control/Expires 则不同,如果检测到本地的缓存还是有效的时间范围内,浏览器会直接使用本地副本,不会发送任何请求。两者一起使用时,Cache-Control/Expires 的优先级要高于 Last-Modified/ETag。

    一般情况下,使用 Cache-Control/Expires 会配合 Last-Modified/ETag 一起使用,因为即使服务器设置缓存时间, 当用户点击“刷新”按钮时,浏览器会忽略缓存继续向服务器发送请求,这时 Last-Modified/ETag 将能够很好利用 304,从而减少响应开销。

  • Last-Modified 与 ETag

    Etag(实体标识)的出现主要是为了解决几个 Last-Modified 比较难解决的问题:首先,Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在 1 秒钟以内,被修改多次的话,它将不能准确标注文件的新鲜度。其次,如果某些文件会被定期生成,当有时内容并没有任何变化,但 Last-Modified 却改变了,导致文件没法使用缓存。

    Etag 是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified 与 ETag 一起使用时,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。

参考文献

彻底理解浏览器缓存机制

浏览器缓存机制详解

IE 浏览器 GET 请求缓存问题