Skip to content

nginx 缓存能够让网页打开速度享受质的飞跃。 当我们第一次访问一个页面时 将下载的css,js,html已经img等相关资源保存在本地。 在第二次,第三次…访问时,就可以不用去下载文件了。

通常来说,设置文件的缓存有两种方式,

  • 一种是在服务器内设置响应头文件
  • 一个是使用h5的manifest文件来进行相关设置

服务器的缓存协商

这种方式设置的缓存有两种,一种是需要服务器验证,另外一种是不用发送请求验证

ETag/Last-Modified

这两种方式做法类似,都要向服务器发送一次请求进行验证。简直,缓存就缓存呗,为什么还要验证呢?

其实,这是该协议的一种特有方式,发送一次验证主要是检查文件是否发生变化

ETag

ETag是用来计算文件的内容是否发生变化,比如,你在文件中删除一个空格,这样都算文件内容发生变化。 通常做法是用md5或者SHA1算法,计算出文件的唯一值。 在前端其实都可以完成, 找到一个文件文件解析的md5算法,然后将文件传入,就可以得到ETag的值。 不过这里,我们着重点并不是让你生成Etag,而是看看ETag在缓存中的重要作用。 ETag是HTTP/1.1A的一种办法,由Web服务器生成,并写入响应头中

//response Headers
ETag:"751F63A30AB5F98F855D1D90D217B356"
//response Headers
ETag:"751F63A30AB5F98F855D1D90D217B356"

接着,到了浏览器之后,便缓存在本地。 当下次打开同样的文章时,会在请求头中发送If-None-Match, 给服务器检查文件是否发生变化。如果没有,则告诉浏览器使用本地的,否则返回新文件

//request Headers
If-None-Match: "751F63A30AB5F98F855D1D90D217B356"
//request Headers
If-None-Match: "751F63A30AB5F98F855D1D90D217B356"

通常情况下,服务器默认是打开Etag的,但是为了防止你的同事,或者后台哥哥的后台配置文件不正确,关闭了Etag,这时候,就需要你对对配置文件做一些设置。 这里我以Nginx为例: 打开ngnix.conf文件,检查是否有以下语句:

etag off;
more_set_headers -s 404 -t 'ETag';
more_clear_headers 'Etag';
etag off;
more_set_headers -s 404 -t 'ETag';
more_clear_headers 'Etag';

Etag打开之后会增加服务器的负载,造成性能的局限性,所以,关闭或者打开Etag都要经过权衡

Last-Modified

Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。 这和文档内容信息验证不同,这里采用的是日期验证办法。 即服务器上会对文件打上一个文件改动的日期,然后客户端接受该日期,下次请求时,返回该日期,服务器验证,如果日期未变,则告诉浏览器使用本地缓存即可。 即在服务器的相应头中,可以设置Last-Modified,来启用这一缓存协议

//Response Header
Last-Modified:Tue, 03 Mar 2015 01:38:18 GMT
//Response Header
Last-Modified:Tue, 03 Mar 2015 01:38:18 GMT

接受到这一响应头之后,浏览器会对该文件做一个缓存,并保存该日期。当下次请求的时候,会通过If-Modified-Since将日期传入并验证:

If-Modified-Since:Tue, 03 Mar 2015 01:38:18 GMT
If-Modified-Since:Tue, 03 Mar 2015 01:38:18 GMT

如果日期未变,则告诉浏览器使用缓存。

那我们通常应该怎样启用服务器这一功能呢? 默认情况下,服务器会对静态资源发送Last-modified的tag。 但是,需要注意,Last-Modified的更新时间只能以秒来计,如果你文件改动过于频繁,Last-Modified是无效的(不过,谁牛逼到1s内能多次更新文件嘞~) 实际上Last-Modified的这个标签的我们通常并不会单独使用它,通常与expires结合,形成一个可降级的缓存. Last-Modified/If-Modified-Since要配合Cache-Control使用

Expires/Cache-Control

Expires/Cache协议与上述验证协议最大的不同在于,他可以省略发送验证请求环节,不需要服务器的验证,而直接使用本地缓存。 通常这种方式,适用于,项目稳定,版本迭代不多的时候

Expires


//Response Headers
Expires:Tue, 03 May 2016 09:33:34 GMT

//Response Headers
Expires:Tue, 03 May 2016 09:33:34 GMT

这告诉浏览器,在2016.5.3号之前,可以直接使用该文本的缓存副本。但是,可能会因为服务器和客户端的GMT时间不同,会有一定的bug。 所以,这里只提议在长时间缓存的情况下使用。否则,应该选择Cache-Control.

以Nginx服务器为例: 对于不经常修改的静态内容(如图片,JS,CSS),可以在服务器中设置expires过期时间,控制浏览器缓存,达到有效减小带宽流量,降低服务器压力的目的

location ~ .*\.(js|css)$ {
expires 10d; 
}
location ~ .*\.(js|css)$ {
expires 10d; 
}

过期时间为10天,js css文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。 通过expires设置过期时间为一天,此时,服务器会根据当前的时间,加上一天.同时添加Expires和Cache-Control头部标签。 即,得到的Response Header为

Expires: Fri, 28 Feb 2016 10:42:09 GMT
Cache-Control: max-age=864000 //24*60*6
Expires: Fri, 28 Feb 2016 10:42:09 GMT
Cache-Control: max-age=864000 //24*60*6

(HTTP规定,如果出现max-age和expires,则max-age默认覆盖掉expires) 当expires为负数表示no-cache,正数或零表示max-age=time。 如果你不想缓存,可以直接设置:

expires -1; //永远过期,Cache-Control: no-cache
expires -1; //永远过期,Cache-Control: no-cache

Cache-Control

这应该是HTTP1.1为了解决HTTP1.0中expires的时间差的bug,而新添加的一个tag. 他的配置项很多,其实完全都可以取代expires(现在大多数服务器都支持). 引用一段原话:

Cache-Control 头在 HTTP/1.1 规范中定义,取代了之前用来定义响应缓存策略的头(例如 Expires)。当前的所有浏览器都支持 Cache-Control,因此,使用它就够了。

General
  Request URL: https://www.sundayle.com/home/images/logo.png
  Request Method: GET
  Status Code: 200  (from memory cache)
  Remote Address: 123.31.46.176:443
  Referrer Policy: no-referrer-when-downgrade
Response Headers
  accept-ranges: bytes
  cache-control: max-age=2592000
  content-length: 3559
  content-type: image/png
  date: Thu, 31 May 2018 03:27:29 GMT
  etag: "5ac1d72f-de7"
  expires: Sat, 30 Jun 2018 03:27:29 GMT
  last-modified: Mon, 02 Apr 2018 07:09:35 GMT
  server: nginx
  status: 200
General
  Request URL: https://www.sundayle.com/home/images/logo.png
  Request Method: GET
  Status Code: 200  (from memory cache)
  Remote Address: 123.31.46.176:443
  Referrer Policy: no-referrer-when-downgrade
Response Headers
  accept-ranges: bytes
  cache-control: max-age=2592000
  content-length: 3559
  content-type: image/png
  date: Thu, 31 May 2018 03:27:29 GMT
  etag: "5ac1d72f-de7"
  expires: Sat, 30 Jun 2018 03:27:29 GMT
  last-modified: Mon, 02 Apr 2018 07:09:35 GMT
  server: nginx
  status: 200

当前每次发送请求之前浏览器会检查缓存系统里,是否有相应文件的备份,如果有的话,则直接从本地模仿一个Response头 理论知识铺垫完毕,我们来take a look. 看看cache-control 有哪些可以配置的属性(以下属性都跟在cache-control后)

  • public: 共有缓存,可被缓存代理服务器缓存,比如CDN
  • private: 私有缓存,这些响应通常只为单个用户缓存,因此不允许任何中间缓存对其进行缓存。例如,用户的浏览器可以缓存包含用户私人信息的 HTML 网页,但 CDN 却不能缓存。
  • max-age=[秒]:表示在这个时间范围内缓存是新鲜的无需更新。类似Expires时间,不过这个时间是相对的,而不是绝对的。也就是某次请求成功后多少秒内缓存是新鲜的。 s-maxage=[秒]:类似max-age, 除了仅应用于共享缓存(如代理)。
  • no-cache:这里不是不缓存的意思,只是每次在使用缓存之前都强制发送请求给源服务器进行验证,检查文件该没改变(其实这里和ETag/Last区别不大)
  • no-store:就是禁止缓存,不让浏览器保留缓存副本
  • must-revalidate:告诉浏览器,你这必须再次验证检查信息是否过期, 返回的代号就不是200而是304了。
  • proxy-revalidate:类似must-revalidate,除了只能应用于代理缓存

可以设置Cache-Control为:

//Response Headers
Cache-Control:private, max-age=0, must-revalidate
//Response Headers
Cache-Control:private, max-age=0, must-revalidate

该文件是一个私有文件,只能被浏览器缓存,而不能被代理缓存。max-age标识该缓存立即过期,其实和no-cache实际上区别不大. 然后must-revalidate告诉浏览器,你必须给我再验证文件过没过期,比如接下来可能会验证Last-Modified或者ETag.如果没有过期则使用本地缓存. 其实上面可以直接等同于:

//Response Headers
Cache-Control:private,no-cache
//Response Headers
Cache-Control:private,no-cache

使用no-store的结果

//Response Headers
Cache-Control:no-store;
//Response Headers
Cache-Control:no-store;

这样表明,不管一不一样都需要重新下载. 强烈表示,不让你使用缓存文件。后续的就不会去验证ETag了。 当然,如果你将IE6那种古老的浏览器考虑进来的话,那你干脆就做的不要脸一点,直接用下面的tag就行:

Cache-Control: no-cache, no-store, must-revalidate //HTTP1.1
Pragma: no-cache //HTTP1.0
Expires: 0 //Proxy
Cache-Control: no-cache, no-store, must-revalidate //HTTP1.1
Pragma: no-cache //HTTP1.0
Expires: 0 //Proxy

nginx怎么配置对应的cache-control头

##设置no-cache
//Nginx
expires -1;
//cache-control
Cache-Control:no-cache

##设置max-age=0
//Nginx
expires 0;
//cache-control
Cache-Control:max-age=0

##设置其他头部
//nginx
add_header  Cache-Control "no-cache";
add_header  Pragma no-cache;
##设置no-cache
//Nginx
expires -1;
//cache-control
Cache-Control:no-cache

##设置max-age=0
//Nginx
expires 0;
//cache-control
Cache-Control:max-age=0

##设置其他头部
//nginx
add_header  Cache-Control "no-cache";
add_header  Pragma no-cache;

上面说的基本上是服务器的响应头,那在浏览器的Request headers里存在cache-control代表什么呢? 当请求头中有:Cache-Control: max-age=0,表示缓存需要进行验证(ETag||Last-Modified),如果缓存未过期,则可以使用。 当请求头中有:Cache-Control: no-cache,表示浏览器只能获取最新的文件。 和Response Header中的no-store相对应。

在开发调试web的时候,经常会碰到因浏览器缓存(cache)而经常要去清空缓存或者强制刷新来测试的烦恼,提供下apache不缓存配置和nginx不缓存配置的设置。在常用的缓存设置里面有两种方式,都是使用add_header来设置:分别为Cache-Control和Pragma。

location ~ .*\.(css|js|swf|php|htm|html )$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
location ~ .*\.(css|js|swf|php|htm|html )$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}

https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn)]

http强制缓存

有一种缓存机制是,服务端通过响应头告诉浏览器,在哪个时间点之前(Expires),或在多长时间之内(Cache-Control: Max-age=…),不要再请求服务器了。这个机制通常称之为 HTTP 强缓存。

一旦资源被判强缓存规则后,再次访问资源会完全没有 HTTP 请求(Chrome 开发者工具的 Network 面板依然会显示请求,但会注明 from cache;Firefox 的 Firebug 也类似,会注明 BFCache),这能够大幅提升性能。所以我们一般会对 CSS、JS、IMG 等资源使用强缓存。

开启强缓存,需要配置如下

location ~ ^/static/ {
    root       /home/site/static;
    etag      on;
    expires 2h;
}
location ~ ^/static/ {
    root       /home/site/static;
    etag      on;
    expires 2h;
}

案例

if ($request_filename ~* ^.*?.(js|html|css)$){
   add_header Cache-Control no-cache;
 }
if ($request_filename ~* ^.*?.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx|jpg|png)$){
    add_header Cache-Control max-age=864000;#缓存一天
}
if ($request_filename ~* ^.*?.(js|html|css)$){
   add_header Cache-Control no-cache;
 }
if ($request_filename ~* ^.*?.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx|jpg|png)$){
    add_header Cache-Control max-age=864000;#缓存一天
}

缓存配置

在用配置如下

   set $origin_root /data/blog/;
   root $origin_root ;

   location @webp_redirect {
       content_by_lua_file "/usr/local/nginx/conf/lua/webp_tmp_path.lua";
   }

   # Feed
   location ~* \.(?:rss|atom)$ {
       expires 1h;
       #add_header Cache-Control max-age=40;
       #add_header Cache-Control "public";
   }

   # CSS and Javascript
   location ~* \.(?:css|js|ttf|woff|woff2)$ {
       add_header X-Content-Type-Options "nosniff";
       add_header X-XSS-Protection "1; mode=block";
       #add_header Cache-Control max-age=50;
       expires 12h;
       #access_log off;
       #add_header Cache-Control "public";
   }

   # Media: images, icons, video, audio, HTC
   location ~* \.(?:gif|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
       expires 7d;
       #add_header Cache-Control max-age=30;
       #access_log off;
       #add_header Cache-Control "public";
   }

   location ~ .*\.(jpg|jpeg|png)$ {
       set $webp_tmp_root /data/webp_data/;
       set $webp_filename $uri$webp_suffix;
       set $webp_filepath $webp_tmp_root$webp_filename;
       set $origin_filepath $origin_root$uri;

       #add_header Cache-Control max-age=20;
       expires 7d;

       # 兼容不支持 webp 的浏览器,直接返回原格式
       if ($webp_filename !~ .*\.webp$) {
           root $origin_root;
       }

# 先去检测是否存在webp文件,如果没有,则内部跳转执行lua转换
       try_files $webp_filename @webp_redirect;
       root $webp_tmp_root;
   }

   location ~ \.(html|htm|xml)$ {
       add_header Cache-Control no-cache;
   }

   location / {
       #include firewall.conf;
       root $origin_root ;
       index index.html;
       if (!-e $request_filename) {
           rewrite ^/(.*)$  / redirect;
       }
   }
   set $origin_root /data/blog/;
   root $origin_root ;

   location @webp_redirect {
       content_by_lua_file "/usr/local/nginx/conf/lua/webp_tmp_path.lua";
   }

   # Feed
   location ~* \.(?:rss|atom)$ {
       expires 1h;
       #add_header Cache-Control max-age=40;
       #add_header Cache-Control "public";
   }

   # CSS and Javascript
   location ~* \.(?:css|js|ttf|woff|woff2)$ {
       add_header X-Content-Type-Options "nosniff";
       add_header X-XSS-Protection "1; mode=block";
       #add_header Cache-Control max-age=50;
       expires 12h;
       #access_log off;
       #add_header Cache-Control "public";
   }

   # Media: images, icons, video, audio, HTC
   location ~* \.(?:gif|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
       expires 7d;
       #add_header Cache-Control max-age=30;
       #access_log off;
       #add_header Cache-Control "public";
   }

   location ~ .*\.(jpg|jpeg|png)$ {
       set $webp_tmp_root /data/webp_data/;
       set $webp_filename $uri$webp_suffix;
       set $webp_filepath $webp_tmp_root$webp_filename;
       set $origin_filepath $origin_root$uri;

       #add_header Cache-Control max-age=20;
       expires 7d;

       # 兼容不支持 webp 的浏览器,直接返回原格式
       if ($webp_filename !~ .*\.webp$) {
           root $origin_root;
       }

# 先去检测是否存在webp文件,如果没有,则内部跳转执行lua转换
       try_files $webp_filename @webp_redirect;
       root $webp_tmp_root;
   }

   location ~ \.(html|htm|xml)$ {
       add_header Cache-Control no-cache;
   }

   location / {
       #include firewall.conf;
       root $origin_root ;
       index index.html;
       if (!-e $request_filename) {
           rewrite ^/(.*)$  / redirect;
       }
   }

缓存知识点

强缓存

  • 强缓存命中则直接读取浏览器本地的资源,在network中显示的是from memory或者from disk
  • 控制强制缓存的字段有:Cache-Control(http1.1)和Expires(http1.0)
  • Cache-control是一个相对时间,用以表达自上次请求正确的资源之后的多少秒的时间段内缓存有效。
  • Expires是一个绝对时间。用以表达在这个时间点之前发起请求可以直接从浏览器中读取数据,而无需发起请求
  • Cache-Control的优先级比Expires的优先级高。前者的出现是为了解决Expires在浏览器时间被手动更改导致缓存判断错误的问题。
  • 如果同时存在则使用Cache-control

强缓存–expires

  • 该字段是服务器响应消息头字段,告诉浏览器在过期时间之前可以直接从浏览器缓存中存取数据。
  • Expires 是 HTTP 1.0 的字段,表示缓存到期时间,是一个绝对的时间 (当前时间+缓存时间)。在响应消息头中,设置这个字段之后,就可以告诉浏览器,在未过期之前不需要再次请求。
  • 由于是绝对时间,用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效,重新请求该资源。此外,即使不考虑修改,时差或者误差等因素也可能造成客户端与服务端的时间不一致,致使缓存失效。
  • 优势特点
    • 1、HTTP 1.0 产物,可以在HTTP 1.0和1.1中使用,简单易用。
    • 2、以时刻标识失效时间。
  • 劣势问题
    • 1、时间是由服务器发送的(UTC),如果服务器时间和客户端时间存在不一致,可能会出现问题。
    • 2、存在版本问题,到期之前的修改客户端是不可知的

强缓存–cache-control

  • 已知Expires的缺点之后,在HTTP/1.1中,增加了一个字段 Cache-control ,该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求。

  • 这两者的区别就是前者是绝对时间,而后者是相对时间。下面列举一些

    Cache-control
    Cache-control

    字段常用的值:(完整的列表可以查看MDN)

    • max-age:即最大有效时间。
    • must-revalidate:如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。
    • no-cache:不使用强缓存,需要与服务器验证缓存是否新鲜。
    • no-store: 真正意义上的“不要缓存”。所有内容都不走缓存,包括强制和对比。
    • public:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)
    • private:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。
  • Cache-control 的优先级高于 Expires,为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段都可以设置。

  • 该字段可以在请求头或者响应头设置,可组合使用多种指令:

    • 可缓存性:
      • public:浏览器和缓存服务器都可以缓存页面信息
      • private:default,代理服务器不可缓存,只能被单个用户缓存
      • no-cache:浏览器器和服务器都不应该缓存页面信息,但仍可缓存,只是在缓存前需要向服务器确认资源是否被更改。可配合private, 过期时间设置为过去时间。
      • only-if-cache:客户端只接受已缓存的响应
    • 到期
      • max-age=:缓存存储的最大周期,超过这个周期被认为过期。
      • s-maxage=:设置共享缓存,比如can。会覆盖max-age和expires。
      • max-stale[=]:客户端愿意接收一个已经过期的资源
      • min-fresh=:客户端希望在指定的时间内获取最新的响应
      • stale-while-revalidate=:客户端愿意接收陈旧的响应,并且在后台一部检查新的响应。时间代表客户端愿意接收陈旧响应 的时间长度。
      • stale-if-error=:如新的检测失败,客户端则愿意接收陈旧的响应,时间代表等待时间。
    • 重新验证和重新加载
      • must-revalidate:如页面过期,则去服务器进行获取。
      • proxy-revalidate:用于共享缓存。
      • immutable:响应正文不随时间改变。
    • 其他
      • no-store:绝对禁止缓存
      • no-transform:不得对资源进行转换和转变。例如,不得对图像格式进行转换。
  • 优势特点

    • 1、HTTP 1.1 产物,以时间间隔标识失效时间,解决了Expires服务器和客户端相对时间的问题。
    • 2、比Expires多了很多选项设置。
  • 劣势问题

    • 1、存在版本问题,到期之前的修改客户端是不可知的

浏览器反馈

1. 打开新窗口

  • public 会从缓存中读取资源, 也就是返回前两种200状态码
  • private, no-cache, no-store 都会重新访问服务器。
  • 如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器,例如

在原窗口按Enter键

  • public 会从缓存中读取资源, 也就是返回前两种200状态码
  • private或must-revalidate 则只有第一次访问时会访问服务器,以后就不再访问。
  • no-cache, no-store 每次都会访问。
  • 如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器

3. 点击刷新按钮

  • 无论将cache-control设置为什么值,浏览器都会重新访问服务器

4. 点击后退按钮

  • public、private、must-revalidate、max-age 都不会重新访问
  • no-cache 则每次都重复访问
  • cache-control是关于浏览器缓存的最重要的设置,因为它覆盖其他设置,比如 expires 和 last-modified。
  • 另外,由于浏览器的行为基本相同,这个属性是处理跨浏览器缓存问题的最有效的方法

协商缓存

协商缓存的状态码由服务器决策返回 200 或者 304

当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存;

如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。

对比缓存在请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点。

协商缓存有 2 组字段(不是两个),控制协商缓存的字段有:

  • Last-Modified/If-Modified-since(http1.0)
  • Etag/If-None-match(http1.1)

Last-Modified/If-Modified-since 表示的是服务器的资源最后一次修改的时间;

Etag/If-None-match 表示的是服务器资源的唯一标识,只要资源变化,Etag就会重新生成。

Etag/If-None-match 的优先级比 Last-Modified/If-Modified-since 高

协商缓存-协商缓存–Last-Modified/If-Modified-since

1.服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间,例如 Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT

2.浏览器将这个值和内容一起记录在缓存数据库中。

3.下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段

4.服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。

优势特点

  • 1、不存在版本问题,每次请求都会去服务器进行校验。服务器对比最后修改时间如果相同则返回304,不同返回200以及资源内容。

劣势问题

  • 1、只要资源修改,无论内容是否发生实质性的变化,都会将该资源返回客户端。例如周期性重写,这种情况下该资源包含的数据实际上一样的。
  • 2、以时刻作为标识,无法识别一秒内进行多次修改的情况。 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
  • 3、某些服务器不能精确的得到文件的最后修改时间。
  • 4、如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用

协商缓存–Etag/If-None-match

为了解决上述问题,出现了一组新的字段 EtagIf-None-Match

Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。

浏览器在发起请求时,服务器返回在 Response header 中返回请求资源的唯一标识。在下一次请求时,会将上一次返回的Etag值赋值给 if-No-Matched 并添加在Request Header中。服务器将浏览器传来的 if-no-matched 跟自己的本地的资源的ETag做对比,如果匹配,则返回304通知浏览器读取本地缓存,否则返回200和更新后的资源。

Etag 的优先级高于 Last-Modified

优势特点

  • 1、可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况。
  • 2、不存在版本问题,每次请求都回去服务器进行校验。

劣势问题

  • 1、计算ETag值需要性能损耗。
  • 2、分布式服务器存储的情况下,计算 ETag 的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时现 ETag 不匹配的情况。( 分布式下反而应该关闭 ETag

缓存状态金字塔

浏览器缓存逻辑图

  • HTTP缓存都是从第二次请求开始的:
    • 第一次请求资源时,服务器返回资源,并在response header中回传资源的缓存策略;
    • 第二次请求时,浏览器判断这些请求参数,击中强缓存就直接200,否则就把请求参数加到 request header 头中传给服务器,看是否击中协商缓存,击中则返回304,否则服务器会返回新的资源

image-20220323170634638

浏览器缓存状态码释义

  • expires, cache-control, last-modified, etag.

image-20220323170714951