0

    浏览器缓存机制浅析--HTTP缓存

    2023.08.01 | admin | 120次围观

    2. Cache-Control

    Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致浏览器缓存机制有几种,如果同时设置的话,其优先级高于Expires。Cache-Control也是一个通用首部字段,这意味着它能分别在请求报文和响应报文中使用。在RFC中规范了 Cache-Control 的格式为:

    1.    "Cache-Control" ":" cache-directive

    作为请求首部时,cache-directive 的可选值有:

    字段名称说明

    no-cache

    告知(代理)服务器不直接使用缓存,要求向原服务器发起请求。

    no-store

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

    max-age=delta-seconds

    告知 (代理)服务器,客户端希望接收一个存在时间不大于detal-seconds秒的资源。

    max-stale[=delta-seconds]

    告知 (代理)服务器,客户端愿意接收一个超过缓存时间的资源,若有定义delta-seconds则为delta-seconds,若没有则为任意超出的时间。

    min-fresh=delta-seconds

    告知 (代理)服务器,客户端希望接收一个在小于delta-seconds秒内被更新过的资源。

    no-transform

    告知 (代理)服务器,客户端希望获取实体数据没有被转换(比如压缩)过的资源。

    only-if-cached

    告知 (代理)服务器,客户端希望获取换成的内容(若有),而不用向原来服务器请求。

    cache-extension

    自定义扩展值,若服务器不识别改值则会被忽略。

    作为响应首部时,cache-directive的可选值有:

    字段名称说明

    public

    表明任何情况下都得缓存该资源(即使是需要HTTP认证的资源)

    Private[="field-name"]

    表明返回报文中全部或者部分(若指定field-name,则为field-name的字段数据)仅开放给某些用户(服务器指定的share-user,如代理服务器)做缓存使用,其他用户则不能缓存这些数据。

    no-cache

    不直接使用缓存,要向服务器发起(新鲜度校验)请求

    no-store

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

    no-transform

    告知客户端缓存文件时不得对实体数据做任何改变。

    only-if-cached

    告知(代理)服务器,客户端希望获取缓存的内容(如果有),而不向原来服务器发起请求。

    must-revalidate

    当前资源一定是向原服务器发起验证请求的,若请求失败会返回504(而非代理服务器上的缓存)

    proxy-revalidate

    与must-revalidate类似,但仅能应用于共享缓存(如代理)

    max-age=delta-seconds

    告知客户端,该资源在delta-seconds秒内是新鲜的,无需向 服务器发起请求。

    s-max-age=delta-seconds

    同max-age,但仅用于共享缓存(如代理)

    cache-extension

    自定义扩展值,若服务器不识别改值则会被忽略。

    如图所示:

    它意味着该资源是从原服务器上取得的,且不做任何缓存。

    当然这种组合的方式也会有些限制,比如 no-cache 就不能和 max-age、min-fresh、max-stale 一起搭配使用。

    组合的形式还能做一些浏览器行为不一致的兼容处理。例如在IE我们可以使用 no-cache 来防止点击“后退”按钮时页面资源从缓存加载,但在 Firefox 中,需要使用 no-store 才能防止历史回退时浏览器不从缓存中去读取数据,故我们在响应报头加上如下组合值即可做兼容处理:

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

    浏览器缓存机制浅析--HTTP缓存

    缓存校验字段

    上述的首部字段均能让客户端决定是否向服务器发送请求,比如设置的缓存时间未过期,那么自然直接从本地缓存取数据即可(在chrome下表现为200 from cache),若缓存时间过期了或资源不该直接走缓存,则会发请求到服务器去。如图所示:

    我们现在要说的问题是,如果客户端向服务器发了请求,那么是否意味着一定要读取回该资源的整个实体内容呢?

    我们试着这么想——客户端上某个资源保存的缓存时间过期了,但这时候其实服务器并没有更新过这个资源,如果这个资源数据量很大,客户端要求服务器再把这个东西重新发一遍过来,是否非常浪费带宽和时间呢?答案是肯定的,那么是否有办法让服务器知道客户端现在存有的缓存文件,其实跟自己所有的文件是一致的,然后直接告诉客户端说“这东西你直接用缓存里的就可以了,我这边没更新过呢,就不再传一次过去了”。

    为了让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,Http1.1新增了几个首部字段来做这件事情。

    1. Last-Modified

    服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。

    客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码即可。

    至于传递标记起来的最终修改时间的请求报文首部字段一共有两个:1.If-Modified-Since: Last-Modified-value

    1.    If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT

    该请求首部告诉服务器如果客户端传来的最后修改时间与服务器上的一致,则直接回送304 和响应报头即可。当前各浏览器均是使用的该请求首部来向服务器传递保存的 Last-Modified 值。

    2. If-Unmodified-Since: Last-Modified-value告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412(Precondition Failed) 状态码给客户端。当遇到下面情况时,If-Unmodified-Since 字段会被忽略:

    2. ETag

    服务器会通过某种算法,给资源计算得出一个唯一标志符(Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。),在把资源响应给客户端的时候,会在实体首部加上“ETag: 唯一标识符”一起返回给客户端。

    客户端会保留该 ETag 字段,并在下一次请求时将其一并带过去给服务器。服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。

    如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。

    那么客户端是如何把标记在资源上的 ETag 传去给服务器的呢?请求报文中有两个首部字段可以带上 ETag 值:1. If-None-Match: ETag-value

    1.    If-None-Match: "56fcccc8-1699"

    告诉服务端如果 ETag 没匹配上需要重发资源数据,否则直接回送304 和响应报头即可。当前各浏览器均是使用的该请求首部来向服务器传递保存的 ETag 值。

    2. If-Match: ETag-value告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412(Precondition Failed) 状态码给客户端。否则服务器直接忽略该字段。If-Match 的一个应用场景是,客户端走PUT方法向服务端请求上传/更替资源,这时候可以通过 If-Match 传递资源的ETag。如果 Last-Modified 和 ETag 同时被使用,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,则要求它们的验证都必须通过才会返回304,若其中某个验证没通过,则服务器会按常规返回资源实体及200状态码。或许你会问为什么它优先?两者功能相似甚至相同,为什么要同时存在?HTTP1.1中ETag的出现主要是为了解决几个Last-Modified比较难解决的问题:

    不能缓存的请求

    当然并不是所有请求都能被缓存。无法被浏览器缓存的请求:

    HTTP信息头中包含Cache-Control:no-cache浏览器缓存机制有几种,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告诉浏览器不用缓存的请求

    需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的

    经过HTTPS安全加密的请求(有人也经过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public之后,能够对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)

    POST请求无法被缓存

    HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存

    用户行为与缓存

    浏览器缓存行为还有用户的行为有关!!!

    用户操作Expires/Cache-ControlLast-Modified/Etag

    地址栏回车

    有效

    有效

    页面链接跳转

    有效

    有效

    新窗口打开

    有效

    有效

    前进、后退

    有效

    有效

    F5刷新

    无效

    有效

    Ctrl+F5

    无效

    无效

    总结

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论