03-HTTP进阶

130 阅读27分钟

HTTP进阶

这一章,我们来详细了解一下HTTP协议中的各种头字段、包括定义、功能、使用方法、注意事项等

HTTP的实体数据

之前我们已经学习了HTTP报文的结构,由header+body组成,但是我们前面主要研究的是header,所以这一节,我们就了解一下body

数据类型与编码

TCP/IP协议中,传输的数据基本上都是header+body的格式,但是TCP和UDP由于是传输层的协议,并不关心body数据是什么,只要把数据发送到对方算是完成任务了

但是HTTP不行,他是应用层协议,数据到达之后必须告诉上层这是什么数据

如果HTTP没有告知浏览器数据类型,那么浏览器看到的就相当于是一个黑盒子,浏览器可以通过猜的方式来检查文件类型,但是这种方式十分低效,而且很大几率检查不出类型

但是,HTTP在诞生之初就已经解决了这个问题,不过是用在电子邮件系统中的,可以发送ASCII码以外的任何数据,这个方法的名字叫做多用途互联网邮件扩展,简称MIME

HTTP只取了这个方法的一部分,用来标记body的数据类型,也就是我们常常听见的MIME type

MIME把数据分成了八大类,每个大类下再细分出多个子类,形式是**type/subtype的字符串**

这里列举一下HTTP中常遇到的几个类别:

  • text文本格式的可读数据,其下的子类有:text/html表示超文本文档,text/plain表示纯文本、text/css表示样式表
  • image图像文件,其下的子类有:image/gifimage/jpegimage/png
  • audio/video音频和视频数据,其下的子类有:audio/mpegvideo/mp4
  • application数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释,其下的子类有:applicatoin/jsonapplication/javascriptapplication/pdf等,另外,如果实在不知道是什么数据,就会是application/octet-stream,即不透明的二进制数据

由于HTTP在传输时为了解决带宽,所以有时候会压缩数据,这时候就会Encoding type告诉数据是用什么编码格式,让对方正确解压

Encoding type的类型有:

  • gzipGNU zip压缩格式,最流行
  • deflatezlib压缩格式,流行程度仅次于gzip
  • br专门为HTTP优化的新压缩算法

数据类型使用的头字段

现在已经有了MIME type 和 Encoding type,所以浏览器和服务器都可以识别出body的数据类型了,但是我们还没将其应用上去,现在我们看看HTTP如何处理

HTTP协议定义了两个Accept请求头字段和两个Content实体头字段,用于客户端和服务器进行内容协商

HTTP传输数据格式.png

接下来我们对这四个字段进行详细的分析:

  • Accept

    标记的是客户端可以理解的MIME type,可以,做分隔符列出多个类型,如:

     Accept: text/html,application/xml,image/png
    
  • Content-Type

    服务器会在响应报文中用该字段告诉实体数据的真实类型,如:

     Content-Type: text/html
    
  • Accept-Encoding(可选):

    该字段标记的是客户端支持的压缩格式,同样可以,列出多个,服务器选择其中一种压缩即可

     Accept-Encoding: gzip, br
    
  • Content-Encoding(可选):

    该字段表示服务器选择的压缩数据方法

     Content-Content: gzip
    

语言类型和编码

上面已经解决了计算机理解body数据的问题,但是现在还有一个问题,就是不同国家的人使用不同的语言,虽然都是text/html,但是如何让浏览器显示出不同的语言

为了解决这个问题,HTTP引入了语言类型和字符集

语言类型就是使用的自然语言,要使用type-subtype的形式,分隔符是-,如:en是任意英语,en-US是美式英语,zh-CN是汉语

由于各个国家会采用不同的字符编码方式,所以这就会导致同样的一段文字,用一种编码显示正常,换另一种编码就出现乱码的情况

为了解决这个情况,后来出现了Unicode和UTF-8,把世界上所有语言都容纳在一种编码方案中,UTF-8也是标准字符集

语言类型使用的头字段

同样,HTTP也使用了Accept请求字段和Content实体字段,用于客户端和服务器语言编码进行内容协商

下面详细分析一下这四个字段:

  • Accept-Language(常用):

    标记了客户端可理解的自然语言,也可以,做分隔符列出多个类型:

     Accept-Language: zh-CN, zh, en
    
  • Content-Language

    告诉客户端实体数据使用的实际语言类型

     Content-Language: zh-CN
    
  • Accept-Charset

    标记了客户端可理解的字符集

     Accept-Charset: gbk, utf-8
    
  • Content-Type(常用):

    这里并没有与Accept-Charset对应的Content-Charset而是使用Content-Type字段在其后面使用charset = xxx来表示

     Content-Type: text/html; charset=utf-8
    

内容协商的质量值

在HTTP中用Accept等请求头字段进行内容协商的时候,还可以使用特殊的q参数表示权重来设置优先级,这里的qquality factor的意思

权重的最大值是1,最小值是0.01,默认值是1,如果是0则表示拒绝,具体的形式是在数据类型或语言代码后面加一个;,然后q=value

还要注意的是;的用法,在大多数编程语言中,;的断句语气要强于,,而在HTTP的内容协商中反了过来,;的意义小于,

例如:

 Accept: text/html,application/xml;q=0.9,*/*;q=0.8

在上述的字段中,表示了浏览器最希望接收到HTML文件,权重是1,其次是XML文件,权重是0.9,最后是任意数据类型,权重是0.8

服务器接收到请求头后,就会计算权重,再根据自己的实际情况优先输出HTML和XML

内容协商的结果

有时候服务器会在响应头中多加一个Vary字段记录服务器在内容协商时参考的请求字段,给出一点信息

 Vary: Accept-Encoding,User-Agent,Accept

Vary字段也可以认为是响应报文的一个特殊的版本标记,同一个URI可能会有不同的版本,主要用在传输链路中间的代理服务器实现缓存服务

HTTP传输大文件的方法

由于现在互联网传输的数据已经可能很大了,所以我们要实现在有限的带宽下高速快捷地传输这些大文件

数据压缩

通常浏览器在发送请求地时候都会带着Accept-Encoding头字段,服务端可以选择一种压缩算法,把源数据压缩

这种方法对于文本文件有较好地压缩率,但是对于图片、视频这一种多媒体数据,本身就是高度压缩了,在压缩也不会变小(甚至会变大一点),所以这种方法不是万能的

不过这种方式压缩文本的效果还是很好的

分块传输

压缩是把大文件变小,而分块传输的思路是化整为零,把大文件分解成很多小块,把小块分批发给浏览器,浏览器收到后在组装复原

这样浏览器和服务器都不用在内存里保存文件的全部,只需要收发一小部分,网络也不会被大文件长时间占用,内存、带宽等资源也就节省了

这种思路在HTTP协议中就是chunked分块传输,在响应报文中用头字段Transfer-Encoding: chunked来表示,意思是报文中的body是分块发送的

分块传输也可以用于流式数据,例如数据库动态生成表单页面,这种情况下body长度位置,无法在Content-Length给出确切的长度,所以只能用chunked方式发送

这里有一个注意点:Transfer-Encoding: chunkedContent-Length这两个字段不能同时存在,因为一个是长度未知,一个是长度已知

接下来我们看一下分块传输的编码规则:

  • 每个分块包含两个部分:长度头和数据块
  • 长度头是以CRLF(回车换行,即\r\n)结尾的一行明文,用16进制表示长度
  • 数据块紧跟在长度头后,也用CRLF结尾,但是数据不包括CRLF
  • 最后用一个长度为0的块表示结束,即0\r\n\r\n

分块传输编码格式.png

范围请求

通过上述的分块传输编码,服务器就可以轻松的收发大文件了

但是假如现在有一个场景,我在看一个电影,我要直接拖动进度条快进,那么这样就相当于获取大文件其中的片段数据,而分块传输并没有这个能力

基于这个问题,HTTP提出了范围请求的概念,允许客户端在请求头中使用专用字段来表示只获取文件的一部分,相当于客户端的化整为零

由于请求范围不是Web服务器必须的,所以服务器必须在响应头中使用字段Accept-Ranges: bytes来告诉客户端该服务器支持范围请求,如果不支持,服务器则可以发送Accept-Ranges: none,或者干脆不发该字段

而对于客户端,则需要使用请求头Range格式是bytes=x-y,其中x和y是以字节为单位的数据范围

这里需要注意,x、y表示的是偏移量,需要从零开始计数,例如前十个字节表示为0-9

并且,Range的格式也很灵活,可以省略x或y,这里举个例子,假设文件是100字节:

  • 0-:表示文档起点到终点,即0-99
  • 10-:表示从第10个字节到文档末尾,即10-99
  • -1:文档的最后一个字节,相当于99-99
  • -10:文档末尾倒数的10个字节,相当于90-99

服务器接收到Range字段后,需要做四件事:

  • 检查范围是否合法,不合法返回状态码416,表示范围请求有误
  • 如果范围正确,则根据Range头计算偏移量读取文件的片段,返回状态码206,表示body只是原数据的一部分
  • 服务器要添加一个响应头字段Content-Range告诉片段的实际偏移量和资源的总大小,格式是**bytes x-y/length**,与Range头区别在于没有=,返回后面多了总长度
  • 最后就是发送数据了,直接把片段用TCP发给客户端,一个范围请求就算是处理完了

请求使用Range字段获取了文件的前32个字节

 GET /16-2 HTTP/1.1
 Host: www.chrono.com
 Range: bytes=0-31

返回的数据

 HTTP/1.1 206 Partial Content
 Content-Length: 32
 Accept-Ranges: btyes
 Content-Range: btyes 0-31/96
 ​
 // this is a plain text json doc

有了范围请求,我们就可以根据视频得时间点计算出文件得Range,不用下载整个文件,直接精确获取分片所在得视频数据

范围请求不仅用在视频进度拖拽,还有常用下载工具中得多段下载断点续传也是基于范围请求实现的,具体要点:

  • 先发个HEAD,看服务器是否支持范围请求,同时获取文件的大小
  • 开N个线程,每个线程使用Range字段划分出各自负责下载的片段,发请求传输数据
  • 意外中断只需要根据上次的下载记录,用Range请求剩下的那一部分就可以了

多段数据

上面我们说了范围请求,请求一次只获取一个片段,其实还支持在Range头中使用多个x-y一次性获取多个片段数据

这种情况需要一种特殊的MIME类型:multipart/byteranges,表示报文的body是由多段字节序列组成的,并且还要用一个参数**boundary=xxx给出段之间的分隔标记**

多段数据的格式如图:

多段数据传输格式.png

请求使用Range字段获取了文件的前10个字节和20到29个字节:

 GET /16-2 HTTP/1.1
 Host: www.chrono.com
 Range: bytes=0-9, 20-29

响应方会返回:

 HTTP/1.1 206 Partial Content
 Content-Type: multipart/byteranges; boundary=000000000001
 Content-Length: 189
 Connection: keep-alive
 Accept-Ranges: btyes
 
 
 --000000000001
 Content-Type: text/plain
 Content-Range: bytes 0-9/96
 
 //this is
 --000000000001
 Content-Type: text/plain
 Content-Range: bytes 20-29/96
 
 //ext json d
 --000000000001--

HTTP的连接管理

短连接

HTTP协议(1.0)的通信通过采用了简单的请求应答模式

底层传输数据需要基于TCP/IP,每次发请求前需要与服务器建立连接,收到响应报文后会立即关闭连接

所以客户端与服务器不会保持长时间连接,也就叫做短连接,早期的HTTP协议被称为无连接协议

而且短链接的缺点很明显,就是传输效率很低,因为要建立连接要三次握手,关闭连接要四次挥手,造成效率降低

长连接

为了解决短连接的缺点,HTTP提出了长连接,也叫做持久连接连接保活连接复用

解决方法采用了成本均摊的思路把本来TCP连接关闭的时间由原来的一个请求应答均摊到多个请求应答上

长连接与短连接对比.png

这样明显,减少了建立连接断开连接的次数,效率自然也就提高了

连接相关的头字段

由于长连接提升性能十分显著,所以在HTTP/1.1中的连接都默认开启长连接

只要服务器发送了第一次请求,后续的请求都会用第一次打开的TCP连接

我们不用手动开启,在请求头里对应的字段是**Connection: keep-alive**

长连接也有缺点,由于TCP连接长时间不关闭,服务器必须在内存中保持他的状态,所以占用了服务器的资源,如果有大量的空闲长连接只连不发,那么很快就会耗尽服务器资源

所以长连接也需要在恰当的时间关闭,不能永远保持与服务器的连接

为了关闭长连接,在客户端中,我们可以在请求头中加上Connection: close字段来关闭连接,服务器收到该字段后,也会在响应报文中加上这个字段,发送之后就调用Socket API关闭TCP连接

而对于服务器,通常不会主动关闭连接,但是也有一些策略,如Nginx:

  • 使用**keepalive_timeout指令,设置超时时间**,超时就会主动断开连接
  • 使用**keepalive_requests指令,设置可发送的最大请求次数**,处理了该请求次数后,就会主动断开连接

另外,还可以头字段中加上**Keep-Alive: timeout=value设置超时时间**,不过该字段约束力不强,所以不常见

队头阻塞

队头阻塞与短连接和长连接无关,而是由HTTP基本的请求应答模型导致的

由于HTTP规定报文必须一收一发,构成一个先进先出的串行队列

所以如果队首的请求因为太慢耽误了时间,那么队列里后面的请求也不得不跟着一起等待

队头阻塞.png

性能优化

由于队头阻塞问题,造成效率低下,所以HTTP又提出一个解决方案:并发连接

对同一个域名发起多个长连接,用数量来解决质量问题

这样确实能够提高效率,但是又带来另外的问题,因为每个客户端都会建立很多个连接,这样服务器资源面对这么多的连接,可能会造成服务器崩溃

对于这个缺陷,HTTP又提出了一个新的技术——域名分片

就是让一台服务器多开几个域名,这样长连接的数量就可以增加了

HTTP的重定向和跳转

重定向的过程

如果需要重定向,第一个发送的请求返回302,表示需要重定向,就会自动发送第二个请求,重定向到Location字段

Location字段属于响应字段,必须出现在响应报文中,但是只有配合301和302状态码才有意义,标记了服务器要求重定向的URI

Location中的URI可以使用绝对URI,也可以使用相对URI,如果是站内跳转,就可以使用相对URI,如果是站外跳转,则必须使用绝对URI

重定向状态码

301:永久重定向,如果浏览器看到301,就会知道原来的URI已经过时了,就会做一些优化,像历史记录,下次可能直接用新的URI访问了,省去再次跳转的成本

302:临时重定向,浏览器只会执行简单的页面跳转,不会有其他的多余动作

303:类似302,但是重定向后请求改为GET方法,避免POST和PUT重复操作

307:类似302,但是重定向后请求中的方法和实体不允许变动

308:类似307,不允许重定向后的请求变动,但是他是301的含义

重定向的相关问题

  • 性能损耗:一个跳转会有两次请求-应答,比正常的访问多了一次
  • 循环跳转:如果重定向的策略出问题,如A->B->C->A,这样就会无限循环,浏览器必须要有检测循跳转的能力,发现这种情况应该立即停止请求

HTTP的Cookie机制

因为服务器在请求完只会就会清理资源,所以不会记住这个请求和相关信息

但是随着现在HTTP应用领域扩大,对记忆能力的需求也越来越强,所以就引入了Cookie机制

服务器会给每个客户端贴上一张小纸条,上面写了只有服务器才能理解的数据,需要的时候客户端把这些信息把给服务器,服务器看到Cookie,就能认出对方了

Cookie的工作过程

这个过程其实就是上述小纸条的传输过程

这里有两个字段:响应头字段Set-Cookie和请求头字段Cookie

用户第一次访问服务器时,服务器会创建一个独特的身份标识,格式是**key=value,然后放进Set-Cookie中,随着响应报文发送给浏览器**

浏览器收到响应报文,看到Set-Cookie字段,就会保存起来下次请求就把该字段放进Cookie字段发送

第二次请求服务器看到请求头中有Cookie,就可以进行个性化服务了

除此之外,服务器还可以在响应头中添加多个Set-Cookie存储多个key=value,但是浏览器发送不需要多个Cookie字段,只需要用;隔开就可以了

Cookie存储在浏览器中,而不是操作系统上

Cookie机制.png

Cookie的属性

  • Cookie生存周期:超过这个期限浏览器认为Cookie失效,则直接在存储中删除

    可以通过两种方式设置:

    1. Expires:过期时间

      用的是绝对时间的,也就是截止日期

       Expires: Thu, 18-Aug-22 15:01:40 GMT
      
    2. Max-Age:相对时间

      单位是秒,浏览器用收到报文的时间点再加上Max-Age,就可以得到失效的绝对时间

       Max-Age=10  //有效期10
  • Cookie作用域让浏览器仅发送给特定的服务器和URI

    Domain和path表示Cookie所属的域名和路径

    浏览器在发送Cookie前会从URI中提取出host和path部分,对比Cookie属性,如果不满足条件,则不会发送Cookie

  • Cookie安全性:尽量不让服务器外的人看到

    这里可以设置三个属性:

    1. HttpOnly:该属性会告诉浏览器,此Cookie只能通过浏览器HTTP协议传输,禁止其他方式访问(如document.cookie等API)
    2. SameSite防范请求跨站伪造(XSRF)攻击,设置成**Same=Strict可以严格限定Cookie不能随着跳转链接跨站发送**,SameSite=Lax则允许GET/HEAD等安全方法,但是禁止POST跨站发送
    3. Secure表示这个Cookie仅能用HTTPS协议加密传输,明文的HTTP协议会禁止发送,但是Cookie本身不是加密的,控制台还是能看到Cookie的各种属性

Cookie的应用

  • 身份识别

    可以保存用户的登录信息,实现会话事务

  • 广告跟踪

    可以读出身份做行为分析,再精准推送广告

HTTP的缓存控制

基于请求-应答模式的特点,大致可以分为客户端缓存和服务器端缓存

服务端的缓存控制

缓存的流程可以简单概括如下:

  • 浏览器发现缓存无数据,于是发送请求,向服务器获取资源
  • 服务器响应请求返回资源,同时标记资源的有效期
  • 浏览器缓存资源等待下次重用

服务器标记资源的有效期使用的头字段是**Cache-Control,其中的值可以是max-age=30,这里的意思是只能缓存30s**,之后就不能用了

这里的**max-age是指生存时间**,时间的计算起点是响应报文的创建时刻,而不是客户端收到报文的时刻

这是HTTP缓存控制的最常用手段,此外还有其他属性能够控制缓存:

  • no_store不允许缓存,用于某些变化频繁的数据
  • no_cache:这里的意思是可以缓存,但是使用之前必须去服务器验证是否过期,是否有最新版本
  • must-revalidate:如果缓存不过期就可以继续使用,但是过期了还想用就必须去服务器验证

服务端缓存.png

客户端的缓存控制

客户端也可以使用Cache-Control进行缓存控制,也就是说请求-应答双方都可以使用这个字段进行缓存控制,互相协商缓存的使用策略

刷新页面其实会重新发送请求,请求头中的**Cache-Control0**

强制刷新页面重新发送请求,请求头中的**Cache-Controlno-cache**

而对于前进、后退、跳转这几个重定向动作,浏览器则用最基本的请求头,所以会检查缓存,直接利用以前的资源,不会进行网络通信

条件请求

通过上述的客户端缓存控制,我们可能会认为浏览器用Cache-Control做缓存控制只能刷新数据,并不能利用缓存数据

实际上,浏览器可以用两个连续的请求组成验证动作先是一个HEAD,获取资源修改的元信息,然后与缓存数据比较,如果没有改动就使用缓存,节省网络流量,否则就再发一个GET请求,获取最新版本

这样发送了两个请求,成本有点高,所以HTTP协议定义了一系列If开头的条件请求,专门用来检查资源是否过期,把两个请求合并在一个请求中

条件请求有5个头字段,最常用的是**If-Modified-SinceIf-None-Match,这两个需要第一次响应报文预先提供Last-modifiedETag,然后第二次请求时就可以带上缓存中的原值,验证资源是否是最新的**

如果资源没有变,服务器则回应一个304,表示缓存依然有效,浏览器可以更新一下有效期,然后直接使用

Last-modified就是文件最后的修改时间

**ETag**是实体标签的缩写,是资源的唯一一个标识,主要用来解决修改时间无法准确区分文件变化的问题

HTTP的代理服务

HTTP代理就相当于一个中间人:

代理服务器.png

代理服务

代理服务就是服务本身不生成内容,而是处于中间位置转发上下游的请求和响应,具有双重身份

面对下游的用户,则表现为服务器,面对上游的源服务器,表现为客户端

这里主要讲最常见的反向代理,它在传输链路中更靠近源服务器,为源服务器提供代理服务

代理的作用

在计算机领域中的任何问题,都可以通过引入一个中间层来解决

由于代理处在HTTP通信过程的中间位置,相应的就对上屏蔽了真实客户端,对下屏蔽了真实服务器,中间层可以为HTTP协议增加更多的灵活性

最基本的功能就是负载均衡,因为面向客户端时屏蔽了源服务器客户端看到的只是代理服务器,源服务器有多少台并不知道所以代理服务器可以分发请求,决定由后面的哪台服务器来响应请求

对于代理常用的负载均衡算法,比如轮询、一致性哈希等等,这些算法的目标都是尽量把外部的流量合理地分散到多台源服务器,提高系统地整体资源利用率和性能

代理服务还能执行更多地功能:

  • 健康检查:利用心跳机制监控服务器,有故障立即踢出集群
  • 安全防护:保护被代理地后端服务器,防止过载和抵御网络攻击
  • 加密卸载:外网使用SSL/TLS加密通信认证,内网则不加密
  • 数据过滤:拦截上下行的数据,对其作相应修改
  • 内容缓存:暂存、复用服务器响应

代理相关头字段

由于代理隐藏了真实客户端和服务器,如果想要这些信息,则需要先用字段Via标明代理的身份

Via这个通用字段可以在请求头或响应头中出现,每经过一个代理节点,代理服务器就会把自身信息追加到字段的末尾

如果有多个中间代理,就会在Via中形成一个链表

Via链表.png

但是这个字段只是解决了判断是否有代理的问题,并不知道源服务器的信息

其实这么做应该是正确的,服务器的IP应该是保密的,不会让用户知道,但是服务器需要知道客户端的真实信息

现在的HTTP标准被没有定义该头字段,但出现了很多事实上的标准,如最常用的X-Forwarded-For和X-Real-IP

  • X-Forwarced-For:形式和Via差不多,但是会在每经过一个代理节点时,追加请求方的IP地址,所以,字段的最左边的IP地址就是客户端的地址
  • X-Real-IP:相当于简单的X-Forwarded-For字段,记录客户端IP地址,没有中间的代理信息

此外,还有两个字段:X-Forwarded-Host和X-Forwarded-Proto作用于X-Real-IP类似,只记录客户端的信息,分别是客户端请求的原始域名和原始协议名

代理协议

上面讲过,X-Forwarded-For可以拿到客户端的信息,但是这个操作代理信息必须要解析HTTP报文头,所以成本比较高,因为代理只需要转发消息就好,不需要解析数据,会降低转发性能

并且,X-Forwarded-For甚至要修改原始报文,但是这在有些情况下是不被允许的(HTTPS)

所以现在出现了专门的代理协议,该协议有v1和v2两个版本,v1和HTTP差不多,也是明文,但是在HTTP报文前面加上了一行ASCII码文本,而v2则是二进制格式

这一行ASCII文本开头是PROXY五个大写字母,然后是TCP4或TCP6,表示客户端IP地址类型,再后面是请求方地址、应答方地址、请求方端口、应答方端口最后一个回车换行结束,如:

 PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n

总之,代理协议可以在不改动原始报文的情况下传递客户端的真实IP

HTTP的缓存代理

缓存代理服务

之前我们的代理只是简单的转发请求,不会存储任何数据,只有最简单的缓存功能

但是我们如果加入缓存,那么代理服务器收到源服务器的响应数据后就要做两件事:转发报文给客户端、将报文存储到自己的Cache中

下次再有相同的请求,代理服务器就可以直接发送304或者缓存数据,不必在从源服务器那里获取,降低了客户端的等待时间,同时节约了源服务器的网络带宽

源服务器的缓存控制

之前服务器端的Cache-Control属性的值为max-age、no_store、no_cache、must-revalidate,这四种缓存属性既可以约束客户端,也可以约束代理

所以,为了区分客户端的缓存和代理的缓存,可以使用两个新属性privatepublic

  • private表示缓存只能在客户端保存,不能放在代理上于别人共享
  • public表示缓存完全开放,谁都可以存和用

还要区分缓存失效后的重新验证,使用must-revalidateproxy-revalidate

  • must-revalidate表示只要过期就必须回源服务器验证
  • proxy-revalidate表示只要求代理的缓存过期后必须验证,客户端不必回源,只验证到代理即可

还要区分缓存的生存时间,使用s-maxagemax_age

  • s-maxage表示限定在代理上存多久
  • max_age表示在客户端上存多久

还有一个代理专有的属性no-transform表示不用对缓存下来的数据优化

HTTP缓存代理.png

客户端的缓存控制

关于缓存生存时间,多了两个新属性max-stalemin-fresh

  • max-stale:表示如果代理商的缓存过期了也可以接收,但是不能过期太多,超过x秒也会不要
  • min-fresh:表示缓存必须有效,并且必须在x秒后依然有效

客户端还有一个only-if-cached属性,表示只接受代理缓存的数据,不接受源服务器的响应,所以如果代理上没有缓存或者缓存过期,则应该给客户端返回504

HTTP客户端缓存代理.png