最初主要是用于传递通过 HTML 封装过的数据。在 1991 年发布了 HTTP0.9 版,在 1996 年发布 1.0 版,1997 年是 1.1 版, 1.1 版也是到今天为止传输最广泛的版本(初始 RFC 2068 在 1997 年发布,,然后在 1999 年被 RFC 2616 取代,再在 2014 年被 RFC 7230/7231/7232/7233/7234/7235 取代), 2015 年发布了 2.0 版,其极大的优化了 HTTP/1.1 的性能和安全性,而 2018 年发布的 3.0 版本,继续优化 HTTP/2 ,激进地使用 UDP 取代 TCP 协议。
HTTP 协议是一种无状态的、应用层的、以请求/应答方式运行的协议,它使用可扩展的语义和自描述消息格式,与基于网络的超文本信息系统灵活的互动。(RFC7230 2014.6)
HTTP 协议使基于 TCP 协议出现的,对 TCP 协议来说, TCP 协议是一个端到端的面向连接的协议,所以 HTTP 在开始传输之前,首先需要建立 TCP 连接,而 TCP 连接的过程需要所谓的“三次握手”。 HTTP 在 TCP 的基础上,规定了 Request-Response 的模式。这个模式决定了通讯必定是由浏览器端首先发起的。
HTTP 协议解决文件传输的问题。HTTP 是应用层协议。
大部分情况下,浏览器的实现者只需要用一个 TCP 库,甚至一个现成的 HTTP 就可以搞定浏览器的网络通讯部分。HTTP 是纯粹的文本协议,它是规定了使用 TCP 协议来传输文本格式的一个应用层协议。
HTTP 是一种不保存状态,即无状态协议。 HTTP 协议自身不对请求和响应之间的通信状态进行保存。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计成如此简单的。
HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能,于是引入了Cookie技术。有了Cookie再用HTTP协议通信,就可以管理状态了。
三次握手
HTTP协议格式
请求报文
请求行
一个典型的请求行比如:
GET www.baidu.com HTTP/1.1
请求方法、资源的路径、协议的版本、可选的请求首部字段和内容实体构成。
HTTP请求方法
- GET:从指定的资源请求数据
- POST:向指定的资源提交要被处理的数据
- HEAD:跟 GET 类似,只返回请求头,不返回报文主体。用于确认 URI 的有效性及资源更新的日期时间等。
- PUT:HTTP1.1 新增。传输文件。就像 FTP 协议的文件上传一样,要求在请求报文的主体中包含文件内容,然后保存到请求 URI 指定的位置。用于新增资源或者使用请求中的有效负载替换目标资源的表现形式。
- DELETE:HTTP1.1 新增。删除文件。是与 PUT 相反的方法。 DELETE 方法按请求 URI 删除指定的资源。
CONNECT:HTTP1.1 新增。现在多用于 HTTPS 和 WebSocket。CONNECT 方法要求在与代理服务器通信时建立隧道,实现用隧道协议进行 TCP 通信。主要使用 SSL 和 TLS 协议把通信内容加密后经网络隧道传输。
CONNECT 代理服务器名:端口号 HTTP版本
OPTIONS:HTTP1.1 新增。返回请求的资源所支持的方法的方法,一般用于调试,多数线上服务不支持。
- TRACE:HTTP1.1 新增。追踪路径,让Web服务器端将之前通信环回给客户端的方法。
- 发送请求时,在Max-Forwards首部字段中填入数值,每经过一个服务器端就将该数字减1,当数值刚好减到0时,就停止继续传输,最后接收到请求的服务端则返回状态码200 OK的响应。
- 客户端通过TRACE方法可以查询发送出去的请求是怎样被加工修改/篡改的。这是因为,请求想要连接到源目标服务器可能会通过代理中转,TRACE方法就是用来确认连接过程中发生的一系列操作。
- TRACE方法容易引发XST(Cross-Site Tracing,跨站追踪)攻击。
在 POST 方法下,表单内容的编码方式enctype
- application/x-www-form-urlencoded
- 数据被编码成以
&
分隔的键值对,同时以=
分隔键和值,字符以 URL 编码方式编码
- 数据被编码成以
- multipart/form-data
- boundary 分隔符
- 每部分表述皆有 HTTP 头部描述子包体,例如 Content-Type
- last boundary 结尾
比较GET与POST
- 传送方式:GET 通过 URL 传输,POST 通过 HTTP 消息主体传输。
- 传输长度:GET 有 URL 的长度限制,这个主要是根据客户端和服务端决定的,Chrome 是 2MB 。POST 请求对数据长度没有要求。
- GET 产生一个 TCP 数据包, POST 产生两个 TCP 数据包。对于 GET 请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200 。而对于 POST ,浏览器先发送 header ,服务器响应 100 continue ,浏览器再发送 data ,服务器响应 200 ok 。
- GET 请求可被缓存,保留再浏览器历史记录中。POST 请求不会被缓存,不会保留在历史记录中。
- GET 请求可被收藏为书签, POST 请求不行
- 刷新、后退等浏览器操作 GET 请求是无害的, POST 请求可能会重复提交表单。
- GET 是安全(这里的安全是指只读特性,就是使用这个方法不会引起服务器状态变化)且幂等(幂等的概念是指同一个请求方法执行多次和执行一次的效果完全相同),而 POST 是非安全非幂等。
比较PUT与POST
PUT 方法是幂等的:连续调用一次或者多次的效果相同(无副作用),而 POST 方法是非幂等的。
响应报文
响应报文由 4 部分组成:
- 响应行
- 响应头
- 空行
- 响应体
响应行
一个典型的HTTP状态如下:
HTTP/1.1 200 OK
第一部分是HTTP版本,第二部分是响应状态码,第三部分是状态码的描述(原因短语)。
状态码和状态文本
常见的状态码
1xx:临时回应,表示客户端请继续。HTTP1.0 不支持
- 100 Continue:上传大文件前使用
- 由客户端发起请求中携带 Expect: 100-continue 头部触发
- 101 Switch Protocols:协议升级使用
- 由客户端发起请求中携带 Upgrade: 头部触发,如升级 websocket 或者 http/2.0
- 102 Processiong:WebDAV 请求可能包含许多涉及文件操作的子请求,需要很长时间才能完成请求。该代码表示服务已经收到并正在处理请求,但无响应可用。这样可以防止客户端超时,并假设请求丢失。
- 100 Continue:上传大文件前使用
2xx:请求成功,表示成功处理了请求的状态代码。
- 200 OK:请求成功
- 201 Created:有新资源在服务器端被成功创建
- 202 Accepted:服务器接收并开始处理请求,但请求未处理完成。这样一个模糊的概念是有意如此设计,可以覆盖更多的场景。例如异步、需要长时间处理的任务
- 203 Non-Authoritative Information(未授权信息):当代理服务器修改了 origin server 的原始响应包体时(例如更换了 HTML 中的元素值),代理服务器可以通过修改 200 为 203 的方式告知客户端这一事实,方便客户端为这一行为作出相应的处理。 203 响应可以被缓存。
- 204 No Content:成功执行了请求且不携带响应包体,并暗示客户端无需更新当前的页面视图。
- 205 Reset Content:成功执行了请求且不携带响应包体,同时指明客户端需要更新当前页面视图。
- 206 Partial Content:使用 range 协议时返回部分响应内容时的响应码,服务器成功处理了部分 GET 请求。
- Content-Range 头部:显示当前片段包体在完整包体中的位置
3xx:表示请求的目标有变化,希望客户端进一步处理,通常,这些状态码用来重定向。
- 300 多种选择:针对请求,服务器可以执行多种操作。服务器可根据请求者(user agent)选择一项操作,或提供操作列表供请求者选择。
- 301 moved permanently:永久性重定向,表示资源已被分配了新的 URL。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
- 302 found:临时性重定向,表示资源临时被分配了新的 URL,HTTP 1.0。服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
- 303 查看其他位置:请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
- 304 not modified:跟客户端缓存没有更新。服务器返回此响应时,不会返回网页内容。
- 305 使用代理:请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
- 307 temporary redirect:临时重定向,和 302 含义相同。
4xx:客户端请求错误
- 400 bad request:请求报文存在错误
- 401 unauthorized:表示发送的请求需要有通过 HTTP 认证的认证信息
- 403 forbidden:表示对请求资源的访问被服务器拒绝
- 404 not found:请求的页面不存在
- 405 方法禁用:禁用请求中指定的方法。
- 406 不接受:无法使用请求的内容特性响应请求的网页。
- 407 需要代理授权:此状态码与 401(未授权)类似,但指定请求者应当授权使用代理。
- 408 Request timeout:客户端请求超时
- 416 Range Not Satisfiable:请求范围不满足实际资源的大小,其中 Content-Range 中的 complete-length 显示完整响应的长度。
- 418:这是一个彩蛋,来自 ietf 的一个愚人节玩笑(超文本咖啡壶控制协议)
5xx:服务端请求错误
- 500 internal server error:服务端错误
- 501 尚未实施:服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
- 502 Bad Gateway:服务器作为网关或代理,从上游服务器收到无效响应。
- 503 service unavailable:服务端暂时性错误,可以一会再试
- 504 网关超时:服务器作为网关或代理,但是没有及时从上游服务器收到请求。
HTTP 头
通用首部字段
请求报文和响应报文两方都会使用的首部
cache-control:通用缓存首部,与强缓存有关。
值 | 含义 |
---|---|
public | 允许在客户端和CDN中缓存资源 |
private | 只有客户端可以缓存它,CDN不能缓存 |
no-cache | 客户端发送的请求中如果包含 no-cache 指令,则表示客户端将不会接收缓存过的响应。 如果服务器返回的响应中包含 no-cache 指令,那么缓存服务器不能对资源进行缓存。 并非禁止缓存,而是需要先与服务器确认返回的响应是否发生了变化,如果资源未发生变化,则可使用缓存副本从而避免下载。 |
no-cache=Location | 由服务器返回的响应中,若报文首部字段 Cache-Control 中对no-cache 字段名具体指定参数值,那么客户端在接收到这个被指定参数值的首部字段对应的响应报文后,就不能使用缓存。换言之,无参数值的首部字段可以使用缓存。只能在响应指令中指定该参数。 |
no-store | 暗示请求(和对应的响应)或响应中包含机密信息。该指令规定缓存不能在本地存储请求或响应的任一部分。 真正意义上的禁止缓存,禁止浏览器以及所有中间缓存存储任何版本的返回响应,每次用户都会向服务器发送请求,并下载完整的响应 |
max-age | 当客户端发送的请求中包含 max-age 指令时,如果判定缓存资源的缓存时间数值比指定时间的数值更小,那么客户端就接收缓存的资源。另外,当指定 max-age 值为 0 ,意味着不能缓存。应用 HTTP/1.1 版本的缓存服务器遇到同时存在 Expires 首部字段的情况时,会优先处理 max-age 指令,而忽略掉 Expires 首部字段。而 HTTP/1.0 版本的缓存服务器的情况却相反, max-age 指令会被忽略掉。 |
s-maxage | s-maxage 指令的功能和 max-age 指令的相同,它们的不同点是 s-maxage 指令只适用于供多位用户使用的公共缓存服务器。当使用 s-maxage 指令后,则直接忽略对 Expires 首部字段及 max-age 指令的处理。 |
min-fresh | 要求缓存服务器返回至少还未过指定时间的缓存资源 |
max-stale | 使用 max-stale 可指示缓存资源,即使过期也照常接收。如果指令未指定参数值,那么无论经过多久,客户端都会接收响应;如果指令中指定了具体数值,那么即使过期,只要仍处于 max-stale 指定的时间内,仍旧会被客户端接收。 |
only-if-cached | 使用only-if-cached 指令表示客户端仅在缓存服务器本地缓存目标资源的情况下才会要求其返回。 |
must-revalidate | 使用must-revalidate 指令,代理会向源服务器再次验证即将返回的响应缓存目前是否仍然有效。另外,使用must-revalidate 指令会忽略请求的max-stale 指令(即使已经在首部使用了max-stale ,也不会再有效果)。 |
proxy-revalidate | proxy-revalidate 指令要求所有的缓存服务器在接收到客户端带有该指令的请求返回响应之前,必须再次验证缓存的有效性。 |
no-transform | 使用 no-transform 指令规定无论是在请求还是响应中,缓存都不能改变实体主体的媒体类型。 |
Connection
作用:
- 控制不再转发给代理的首部字段
- 管理持久连接 | 值 | 含义 | | —- | —- | | keep-alive | 复用连接,长连接。 | | close | HTTP/1.1 版本的默认连接都是持久连接。为此,客户端会在持久连接上连续发送请求。当服务器端想明确断开连接时,则指定 Connection 首部字段的值为 Close 。 HTTP/1.1 之前的 HTTP 版本的默认连接都是非持久连接。 |
持久连接:只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。
Date
表明创建 HTTP 报文的日期和时间。
HTTP/1.1 协议使用在 RFC1123 中规定的日期时间的格式。例如:Date: Tue, 03 Jul 2012 04:40:59 GMT
之前的 HTTP 协议版本周使用在 RFC850 中定义的格式。例如:Date: Tue 03-Jul-12 04:40:59 GMT
Transfer-Encoding
规定了传输报文主体时采用的编码方式
Via
由代理服务器添加,显示了报文经过的中间节点(代理、网关)
Upgrade
升级为其他协议
Wraning
错误和警告通知
Trailer
报文末端的首部一览
Pragma
报文指令。
请求首部
从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。
安全请求头
AuthorizationAuthorization: Basic dwVub3NlbjpwYXNzd29yZA==
用来告知服务器,用户代理的认证信息(证书值)。通常,想要通过服务器认证的用户代理会在接收到返回的 401 状态码响应后,把首部字段 Authorization 加入请求中。共用缓存在接收到含有 Authorization 首部字段的请求时的操作处理会略有差异。
Cookie
客户端用它来向服务器传送一个令牌 —— 它并不是真正的安全首部,但确实隐含了安全功能。
Cookie2
用来说明请求端支持的 cookie 版本。
Accept
表示浏览器端接受的格式。
可通知服务器,用户代理能够处理的媒体类型及媒体类型的相关优先级。可使用 type/subtype
这种形式,一次指定多种媒体类型。
若想要给现实的媒体类型增加优先级,则使用 q=
来额外表示权重值,用分号(;
)进行分隔。权重值 q 的范围是0~1(可精确到小数点后3位),且 1 为最大值。不指定权重 q 值时,默认权重为 q=1.0 。当服务器提供多种内容时,将会首先返回权重值最高的媒体类型。
值 | 含义 |
---|---|
text/html | |
text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 |
Accept-CharsetAccept-Charset: iso-8859-5,unicode-1-1;q=0.8
可用来通知服务器用户代理支持的字符集及字符集的相对优先顺序。另外,可一次性指定多种字符集。
Accept-Encoding
值:gzip, deflate
用来告知服务器用户代理支持的内容编码及内容编码的优先级顺序。可一次性指定多种内容编码。
值 | 含义 |
---|---|
gzip | 由文件压缩程序 gzip (GNU zip)生成的编码格式(RFC1952),采用 Lempel-Ziv 算法(LZ77)及 32 位循环冗余校验(CRC) |
compress | 由 UNIX 文件压缩程序 compress 生成的编码格式,采用 Lempel-Ziv-Welch 算法(LZW) |
deflate | 组合使用 zlib (RFC1950)及由 deflate 压缩算法(RFC1951)生成的编码格式 |
identify | 不执行压缩或不会变化的默认编码格式 |
Accept-LanguageAccept-Language: zh-cn,zh;q=0.7,en-us,en;q=0.3
浏览器端接受的的自然语言集(指中文或英文等),以及自然语言集的相对优先级。可一次指定多种自然语言集。
Expect
Expect: 100-continue
客户端使用首部字段 Expect 来告知服务器,期望出现的某种特定行为。因服务器无法理解客户端的期望作出回应而发生错误时,会返回状态码 417 Expectation Failed
。客户端可以利用该首部字段,写明所期望的扩展。虽然 HTTP/1.1 规范只定义了 100-continue (状态码100 Continue之意)。
From
From: info@hackr.jp
用来告知服务器使用用户代理的用户的电子邮件地址。通常,其使用目的就是为了显示搜索引擎等用户代理的负责人的电子邮件联系方式。使用代理时,应尽可能包含 From 首部字段(但可能会因代理不同,将电子邮件地址记录在User-Agent 首部字段内)。
Host
虚拟主机运行在同一个 IP 上,因此使用首部字段 Host 加以区分Host: www.hackr.jp
HTTP 访问使用的域名。首部字段 Host 会告知服务器,请求的资源所处的互联网主机名和端口号。 Host 首部字段在 HTTP/1.1 规范内是唯一一个必须被包含在请求内的首部字段。如服务器未设定主机名,那直接发送一个空值即可。
缺少 Host 头部,或者超过一个 Host 头部,或者不合法的 Host 头部都会导致返回 400 状态码。
If-Match
形如 If-xxx
这种歌样式的请求首部字段,都可称为条件请求。服务器接收到附带条件的请求后,只有判断指定条件为真时,才会执行请求。
首部字段 If-Match ,属附带条件之一,它会告知服务器匹配资源所用的实体标记(ETag)值。这时的服务器无法使用弱 ETag 值。服务器会比对 If-Match 的字段值和资源的 ETag 值,仅当两者一致时,才会执行请求。反之,则返回状态码 412 Precondition Failed 的响应。还可以使用星号指定 If-Match 的字段值。针对这种情况,服务器将会忽略 ETag 的值,只要资源存在就处理请求。
If-Modified-Since
如果在 If-Modified-Since 字段指定的日期时间后,资源发生了更新,服务器会接受请求。
首部字段 If-Modified-Since ,属附带条件之一,它会告知服务器若 If-Modified-Since 字段值早于资源的更新时间,则希望能处理该请求。而在指定 If-Modified-Since 字段值的日期时间之后,如果请求的资源都没有过更新,则返回状态码 304 Not Modified 的响应。If-Modified-Since 用于确认代理或客户端拥有的本地资源的有效性。获取资源的更新日期时间,可通过确认首部字段 Last-Modified 来确定。
If-None-Match
只有在 If-None-Match 的字段值与 ETag 值不一致时,可处理该请求。与 If-Match 首部字段的作用相反。
在 GET 或 HEAD 方法中使用首部字段 If-None-Match 可获取最新的资源。因此,这与使用首部字段 If-Modified-Since 时有些类似。
If-Range
If-Range 字段值若是跟 ETag 值或更新的日期时间匹配一致,那么就作为范围请求处理。若不一致,则忽略范围请求,返回全部资源。如果不适用 If-Range 则需要进行两次处理。
If-Unmodified-Since
首部字段 If-Unmodified-Since 和首部字段 If-Modified-Since 的作用相反。它的作用的时告知服务器,指定的请求资源只有在字段值内指定的日期时间之后,未发生更新的情况下,才能处理请求。
Max-Forwards
通过 TRACE 方法或 OPTIONS 方法,发送包含首部字段 Max-Forwards
的请求时,该字段以十进制整数形式指定可经过的服务器最大数目。服务器在往下一个服务器转发请求之前,Max-Forwards
的值减 1 后重新赋值。当服务器接收到 Max-Forwards
值为 0 的请求时,则不再进行转发,而是直接返回响应。
Proxy-Authorization
接收到从代理服务器发来的认证质询时,客户端会发送包含首部字段 Proxy-Authorization 的请求,以告知服务器认证所需要的信息。
Range
允许服务器基于客户端的请求只发送响应包体的一部分给到客户端,而客户端自动将多个片断的包体组合成完整的体积更大的包体。
- 支持断点续传
- 支持多线程下载
- 支持视频播放器实时拖动
对于只需获取部分资源的范围请求,包含首部字段 Range 即可告知服务器资源的指定范围。接收到附带 Range 首部字段请求的服务器,会在处理请求之后返回状态码为 206 Partial Content 的响应。无法处理该范围请求时,则会返回状态码 200 OK 的响应及全部资源。
例如:
基于字节,设包体总长度为 10000
- 第一个 500 字节:bytes=0-499
- 第二个 500 字节
- bytes=500-999
- bytes=500-600,601-999
- bytes=500-700,601-999
- 最后一个 500 字节:
- bytes=-500
- bytes=9500-
- 仅要第一个和最后一个字节:bytes=0-0,-1
通过Range头部传递请求范围,如:Range:bytes=0-499
相关的响应码:206、416、200(服务器不支持 Range 请求时)
多重范围:
请求:Range:bytes=0-50,100-150
响应:Content-Type: multipart/byteranges;boundary=…
首部字段 Referer 会告知服务器请求的原始资源 URI 。客户端一般都会发送 Referer 首部字段给服务器。但当直接在浏览器的地址输入 URI ,或出于安全性的考虑时,也可以不发送该首部字段。
Referer 不会被添加的场景:
- 来源页面采用的协议为表示本地文件的 file 或者 data uri
- 当前请求页面采用的是 http 协议,而来源页面采用的是 https 协议
服务器端常用于统计分析、缓存优化、防盗链等功能。
TE
首部字段 TE 会告知服务器客户端能够处理响应的传输编码方式及相对优先级。它和首部字段 Accept-Encoding 的功能很相像,但是用于传输编码。首部字段 TE 除指定传输编码之外,还可以指定伴随 trailer 字段的分块传输编码的方式。应用后者时,只需把 trailers 赋值给该字段值。
User-Agent
首部字段 User-Agent 会将创建请求的浏览器和用户代理名称等信息传达给服务器。由网络爬虫发起请求时,有可能会在字段内添加爬虫作者的电子邮件地址。此外,如果请求经过代理,那么中间也很可能被添加上代理服务器的名称。
例如:User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:66.0) Gecko/20100101 Firefox/66.0
响应首部
从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。
Accept-Ranges
当不能处理范围请求时,Accept-Ranges:none
首部字段 Accept-Ranges 是用来告知客户端服务器是否能处理范围请求,以指定获取服务器端某个部分的资源。可指定的字段值有两种,可处理范围请求时指定其为 bytes ,反之则指定其为 none 。
Age
首部字段 Age 能告知客户端,源服务器在多久前创建了响应。字段值的单位为秒。若创建该响应的服务器时缓存服务器, Age 值是指缓存后的响应再次发起认证到认证完成的时间值。代理创建响应时必须加上首部字段 Age 。
ETag
页面的信息摘要,用于判断是否需要重新到服务端取回页面
首部字段 ETag 能告知客户端实体标识。它是一种可将资源以字符串形式做唯一性标识的方式。服务器会为每份资源分配对应的 ETag 值。另外,当资源更新时, ETag 值也需要更新。生成 ETag 值时,并没有统一的算法规则,而仅仅是由服务器来分配。资源被缓存时,就会被分配唯一性标识。若在下载过程中出现连接中断、再连接的情况,都会依照 ETag 值来指定资源。
- 强 ETage 值:不论实体发生多么细微的变化都会改变其值。
- 弱 ETag 值:只用于提示资源是否相同。只有资源发生了根本改变,产生差异时才会改变 ETag 值。这时,会在字段最开始处附加 W/ 。
Location
使用首部字段 Location 可以将响应接收方引导至某个与请求 URI 位置不同的资源。
Proxy-Authenticate
把由代理服务器所要求的认证信息发送给客户端。
Retry-After
告知客户端应该在多久之后再次发送请求。主要配合状态码 503 Service Unavailable
响应,或 3xx Redirect
响应一起使用。字段值可以指定为具体的日期时间(GMT等格式),也可以是创建响应后的秒数。
Server
首部字段 Server 告知客户端当前服务器上安装的 HTTP 服务器应用程序的信息。不单单会标出服务器上的软件应用名称,还有可能包括版本号和安装时启用的可选项。
Vary
当代理服务器接收到带有 Vary 首部字段指定获取资源的请求时,如果使用的 Accept-Language 字段的值相同,那么就直接从缓存返回响应。反之,则需要先从源服务器端获取资源后才能作为响应返回。
首部字段 Vary 可对缓存进行控制。源服务器会向代理服务器传达关于本地缓存使用方法的命令。从代理服务器接收到源服务器返回包含 Vary 指定项的响应之后,若再要进行缓存,仅对请求中含有相同 Vary 指定首部字段的请求返回缓存。即使对相同资源发起请求,但由于 Vary 指定的首部字段不相同,因此必须要从源服务器重新获取资源。
WWW-Authenticate
首部字段 WWW-Authenticate 用于 HTTP 访问认证。它会告知客户端适用于访问 URI 所指定资源的认证方案( Basic 或是 Digest )和带参数提示的质询( challenge )。状态码 401 Unauthorized
响应中,肯定带有首部字段 WWW-Authenticate 。
实体首部字段
针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。比如,可以用实体首部来说明实体主体部分的数据类型。
Allow
首部字段 Allow 用于通知客户端能够支持 Request-URI 指定资源的所有 HTTP 方法。当服务器接收到不支持的 HTTP 方法时,会以状态码 405 Method Not Allowed
作为响应返回。
Content-Encoding
首部字段 Content-Encoding 会告知客户端服务器对实体的主体部分选用的内容编码方式。内容编码是指在不丢失实体信息的前提下所进行的压缩。
Content-Language
首部字段 Content-Language 会告知客户端,实体主体使用的自然语言(指中文或英文等语言)。
Content-Length
首部字段Content-Length表明了实体主体部分的大小(单位是字节)。对实体主体进行内容编码传输时,不能再使用Content-Length首部字段。
Content-Location
首部字段Content-Location给出与报文主体部分相对应的URI。和首部字段Location不同,Content-Location表示的是报文主体返回资源对应的URI。
Content-MD5
客户端会对接收的报文主体执行相同的MD5算法,然后与首部字段Content-MD5的字段值比较。
首部字段Content-MD5是一串由MD5算法生成的值,其目的在于检查报文主体在传输过程中是否保持完整,以及确认传输到达。
Content-Range
针对范围请求,返回响应时使用的首部字段Content-Range,能告知客户端作为响应返回的实体的哪个部分符合范围请求。字段值以字节为单位,表示当前发送部分及整个实体大小。
Content-Type
首部字段 Content-Type 说明了实体主体内对象的媒体类型。和首部字段 Accept 一样,字段值用 type/subtype 形式赋值。
值 | 含义 |
---|---|
application/json | 请求体中的数据会以json字符串的形式发送到后端,是 axios 默认的请求头 content-type 类型,例子{“title”: “test”, “sub”: [1,2,3]} |
application/x-www-form-urlencoded | 请求体中的数据会以普通表单形式(键值对)发送到后端,例子key=val1&key2=val2 |
multipart/form-data | 它会将请求体的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。 |
text/xml |
在 axios 中,如何配置这几种 content-type:https://www.yuque.com/demons/nouxko/cl0znn#zaKRe
Expires
首部字段 Expires 会将资源失效的日期告知客户端。缓存服务器在接收到含有首部字段 Expires 的响应后,会以缓存来应答请求,在 Expires 字段值指定的时间之前,响应的副本会一直被保存。当超过指定的时间后,缓存服务器在请求发送过来时,会转向源服务器请求资源。
但是,当首部字段 Cache-Control 有指定 max-age 指令时,比起首部字段 Expires ,会优先处理 max-age 指令。
Last-Modified
首部字段Last-Modified指明资源最终修改的时间。一般来说,这个值就是Request-URI指定资源被修改的时间。
其它首部字段
X-Frame=Options
首部字段X-Frame-Options属于HTTP响应首部,用于控制网站内容在其他Web网站的Frame标签内的显示问题。其主要目的是为了防止点击劫持攻击。首部字段X-Frame-Options有以下两个可指定的字段值。
- DENY:拒绝
SAMEORIGIN:仅同源域名下的页面匹配时许可。
X-XSS-Protection
首部字段X-XSS-Protection属于HTTP响应首部,它是针对跨站脚本攻击(XSS)的一种对策,用于控制浏览器XSS防护机制的开关。首部字段X-XSS-Protection可指定的字段值如下。0:将XSS过滤设置成无效状态
- 1:将XSS过滤设置成有效状态
DNT
首部字段DNT属于HTTP请求首部,其中DNT是Do Not Track的简称,意为拒绝个人信息被收集,是表示拒绝被精准广告追踪的一种方法。首部字段DNT可指定的字段值如下。
- 0:同意被追踪
- 1:拒绝被追踪
P3P
首部字段P3P属于HTTP响应首部,通过利用P3P技术,可以让Web网站上的个人隐私变成一种仅供程序可理解的形式,以达到保护用户隐私的目的。要进行P3P的设定,需按以下操作步骤进行。
- 创建P3P隐私
- 创建P3P隐私对照文件后,保存命名在/w3c/p3p.xml
- 从P3P隐私中新建Compact policies后,输出到HTTP响应中
X-Forwarded-For
是一个 HTTP 扩展头部,用来表示 HTTP 请求端真实 IP
X-Forwarded-For: client, proxy1, proxy2
Set-Cookie
属性 | 说明 |
---|---|
NAME=VALUE | 赋予Cookie的名称和其值(必须项) |
expires=DATE | Cookie的有效期(若不明确指定则默认为浏览器关闭前为止) |
max-age | cookie 经过xx 秒后失效,max-age优先级高于expires |
path=PATH | 将服务器上的文件目录作为Cookie的适用对象(若不指定则默认为文档所在的文件目录) |
domain=域名 | 作为Cookie适用对象的域名(若不指定则默认为创建Cookie的服务器的域名) |
Secure | 仅在HTTPS安全通信时才会发送Cookie |
HttpOnly | 加以限制,使Cookie不能被JavaScript脚本访问 |
HTTP 0.9
最传统的 request - response 的模式,HTTP 0.9 版本的协议请求时,不支持请求头,只支持 GET 方法。
HTTPHTTP1.0
HTTP 1.0 主要增加了几个变化:
- 在请求中加入了 HTTP 版本号,如: GET/coolshell/index.html HTTP/1.0
- HTTP 开始有 header 了,不管是 request 还是 response
- 增加了 HTTP Status Code 标识相关的状态码
- Content-Type 可以传输其他的文件了
主要缺点:每请求一个文档都要新建一个 TCP 链接,而且是串行请求,所以,就算网络变快了,打开网页的速度还是很慢。另一种开销就是万维网客户和服务器为每一次建立新的 TCP 连接都要分配缓存和变量。
HTTP/1.1
主要解决了 HTTP 1.0 的网络性能的问题,以及增加了一些新的东西:
- 可以设置 keepalive 来让 HTTP 重用 TCP 连接,重用 TCP 连接可以省了每次请求都要进行三次握手的开销。这就是所谓的 “HTTP 长连接” 或是请求响应式的 “HTTP 持久连接”。
- 支持 pipeline 网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。(注:非幂等的 POST 方法或是有依赖的请求是不能被 pipeline 化的)
- 支持 Chunked Responses ,也就是说,在 Response 的时候,不必说明 Content-Length ,这样,客户端就不能断连接,直到收到服务端的 EOF 标识。这种技术又叫“服务端 Push 模型”,或是“服务端 Push 式的 HTTP 持久连接”
- 增加了 cache-control 机制
- 协议头注增加了 Language ,Encoding ,Type 等等头,让客户端可以跟服务器端进行更多的协商。
- 正式加入了一个很重要的头 —— HOST。
- 正式加入了 OPTIONS 方法。
使用持续连接。所谓持续连接就是万维网服务器在发送响应后仍然在一段时间内保持这条连接,使同一个客户(浏览器)和该服务器可以继续在这条连接上传送后续的 HTTP 请求报文和响应报文。
keep-alive 的优点:
- 较少的 CPU 和内存的使用(由于同时打开的连接减少了)
- 允许请求和应答的 HTTP 管线化
- 降低拥塞控制(TCP 连接减少了)
- 减少了后续请求的延迟(无需再进行握手)
HTTP 2
HTTP 1.1 虽然可以重用 TCP 连接,但是请求还是一个一个串行发的,需要保证其顺序。然而,大量的网页请求中都是些资源类的东西,这些东西占了整个 HTTP 请求中最多的传输数据量。
HTTP 2 和 HTTP 1.1 最主要的不同是:
- HTTP 2 是一个二进制协议,增加了数据传输的效率
- HTTP 2 式可以在一个 TCP 连接中并发请求多个 HTTP 请求(多路复用):只需一个连接即可实现并行
- HTTP 2 会压缩头,如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的部分。这就是所谓的 HPACK 算法(头部压缩)
- HTTP 2 允许服务端在客户端放 cache ,又叫服务端推送 ,也就是说,你没有请求的东西,我服务端可以先送给你放在你的本地缓存中。服务端推送能够在客户端发送第一个请求到服务端时,提前把一部分内容推送到客户端,放入缓存当中,这可以避免客户端请求顺序带来的并行度不高,从而导致的性能问题
TCP 连接复用,则使用同一个 TCP 连接来传输多个 HTTP 请求,避免了 TCP 连接建立时的三次握手开销,和初建 TCP 连接时传输窗口小的问题
二进制分帧
帧: HTTP/2 数据通信的最小单位信息,指 HTTP/2 中逻辑上的 HTTP 消息。例如请求和响应等,消息由一个或多个帧组成。
流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数 ID 。
HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。
多路复用
HTTP 1.1 基于串行文件传输数据,因此这些请求必须是有序的,所以实际上我们只是节省了建立连接的时间,而获取数据的时间并没有减少。
假设我们在 Apache 中设置了最大并发数 200 ,而因为浏览器本身的限制,最大请求数为 6 ,那么服务器能承载的最高并发数是 50 。
而 HTTP 2.0 引入了二进制数据帧和流的概念,其中帧对数据进行顺序标识,这样浏览器收到数据之后,就可以按照序列对数据进行合并,而不会出现合并后数据错乱的情况。同样是因为有了序列,服务器就可以并行的传输数据。
多路复用意味着来自很多流的数据包能够混合在一起通过同样连接传输。当到达终点时,再根据不同帧首部的流标识符重新连接不同的数据流进行组装。
头部压缩
HTTP 1.x 的头带有大量信息,而且每次都要重复发送。 HTTP/2 使用 encoder 来减少需要传输的 header 大小,通讯双方各自缓存一份头部字段表,既避免了重复 header 的传输,又减少了需要传输的大小。可以理解为只发送差异数据,而不是全部发送,从而减少头部的信息量。
需要注意的是,HTTP/2 关注的是首部压缩,而我们常用的 gzip 等是报文内容(body)的压缩,二者不仅不冲突,且能够一起达到更好的压缩效果。
服务端推送
服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。并且,服务端推送能把客户端所需要的资源伴随着 index.html 一起发送到客户端,省去了客户端重复请求的步骤。
正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。
服务端推送还有一个很大的优势:可以缓存在遵循同源的情况下,不同页面之间可以共享缓存资源。
当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 PUSH——PROMISE 的 Frame ,里面带了 PUSH 需要新建的 Stream ID 。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH——PROMISE 类型,便会准备接收服务端要推送的流。
不适用 HTTP/2 的性能优化
- JS 文件的合并
- 多域名提高浏览器的下载速度
HTTP/2 性能瓶颈
因为现在所有的压力集中在底层一个 TCP 连接之上, TCP 很可能就是下一个性能瓶颈,比如 TCP 分组的队首阻塞问题,单个 TCP packet 丢失导致整个连接阻塞,无法逃避,此时所有消息都会受到影响。
HTTP 3
TCP 协议的不足和 UDP 的一些优点:
- 基于 TCP 开发的设备和协议非常多,兼容困难
- TCP 协议栈是 Linux 内部的重要部分,修改和升级成本很大
- UDP 本身是无连接的、没有建链和拆链成本
- UDP 的数据包无队头阻塞问题
- UDP 改造成本小
谷歌决定在 UDP 基础上改造一个具备 TCP 协议优点的新协议,这个新协议就是 QUIC 协议。
QUIC 协议最初由 Google 的 Jim Roskind 设计,实施并于 2012 年部署,在 2013 年随着实验的扩大而公开宣布,并向 IETF 进行了描述。 QUIC 提高了当前正在使用 TCP 的面向连接的 Web 应用程序的性能。它在两个端点之间使用用户数据报协议(UDP)建立多个复用连接来实现此目的。 QUIC 的次要目标包括减少连接和传输延迟,在每个方向进行带宽估计以避免拥塞。它还将拥塞控制算法移动到用户空间,而不是内核空间,此外使用前向纠错(FEC)进行扩展,以在出现错误时进一步提高性能。
HTTP 3.0 又称为 HTTP Over QUIC,其弃用 TCP 协议,改为使用基于 UDP 协议的 QUIC 协议来实现。
队头阻塞(head-of-line-blocking):是计算机网络中一种性能受限的现象。
- HTTP 2.0 协议的多路复用机制解决了 HTTP 层的队头阻塞问题,但是在 TCP 层仍然存在队头阻塞问题。
- TCP 协议在收到数据包之后,这部分数据可能是乱序到达的,但是 TCP 必须将所有数据收集排序整合后给上层使用,如果其中某个包丢失了,就必须等待重传,从而出现某个丢包数据阻塞整个连接的数据使用。
- QUIC 的多路复用和 HTTP2 类似。在一条 QUIC 连接上可以并发发送多个 HTTP 请求。但是,QUIC 的多路复用比 HTTP/2 有一个很大的优势
- QUIC 一个连接上的多个 stream 之间没有依赖。这样加入 stream2 丢了一个 udp packet ,也只会影响 stream2 的处理。不会影响 stream2 之前及之后的 stream 的处理。
- QUIC 协议是基于 UDP 协议实现的,在一条链接上可以有多个流,流于流之前是互不影响的,当一个流出现丢包影响范围非常小,从而解决队头阻塞问题。
HTTPS
HTTP 的缺点:
- 通信使用明文(不加密),内容可能会被窃听
- 不验证通信方的身份,因此有可能遭遇伪装
- 无法证明报文的完整性,所以有可能已遭篡改
对称加密和非对称加密
对称加密:即通信的双方都使用同一个密钥进行加解密。包括 DES、Triple-DES、RC2 和 RC4 。简单性能也好,但是容易被 hacker 拦截密钥。
非对称加密:
- 私钥 + 公钥 = 密钥对
- 即用私钥加密的数据,只有对应的公钥才能解密,用公钥加密的数据,只有对应的私钥才能解密
- 包括 RSA
- 安全性高,但是性能低。
解决方案:结合两种加密方式,将对称加密的密钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用私钥进行解密得到对称加密的密钥,然后双方可以使用对称加密来进行沟通。
数字签名
除了加 / 解密报文之外,还可以用加密系统对报文进行签名,以说明是谁编写的报文,同时证明报文未被篡改过。这种技术称为数字签名。
数字签名通常是用非对称公开密钥技术产生的。
数字证书
数字证书中包含了由某个受信任组织担保的用户或公司的相关信息。
数字证书中一般包含:
- 签发者
- 证书用途
- 使用者公钥
- 使用者私钥
- 使用者的 HASH 算法
- 证书到期时间等
SSL采用一种叫做公开密钥加密的加密处理方式。公开密钥加密使用一对非对称的密钥。一把叫做私有密钥,另一把叫做公开密钥。使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。
HTTP 协议中没有加密机制,但可以通过和 SSL(Secure Socket Layer,安全套接层)或 TLS(Transport Layer Security,安全层传输协议)的组合使用,加密 HTTP 的通信内容。 用 SSL 建立安全通信线路之后,就可以在这条线路上进行 HTTP 通信了。与 SSL 组合使用的 HTTP 被称为 HTTPS(HTTP Secure,超文本传输安全协议)或HTTP over SSL。在采用SSL后,HTTP就拥有了HTTPS的加密、证书和完整性保护这些功能。
虽然使用HTTP协议无法确定通信方,但如果使用 SSL 则可以。 SSL 不仅提供加密处理,而且还使用了一种被称为证书的手段。证书由值得信任的第三方机构颁发,用以证明服务器和客户端是实际存在的。
使用 HTTPS 时,所有的 HTTP 请求和响应数据在发送到网络之前,都要进行加密。SSL 工作在 OSI 七层模型中的表示层, TCP/IP 四层模型的应用层。
认证方式实现
数字证书
数字签名是附加在报文上的特殊加密校验码,可以证明是作者编写了这条报文,前提是作者才会有私钥,才能算出这些校验码。如果传输的报文被篡改,则校验码不会匹配,因为校验码只有作者保存的私钥才能产生,所以前面可以保证报文的完整性。
数字证书认证机构(Certificate Authority CA)是客户端和服务器双方都可信赖的第三方机构。
HTTPS通信步骤
- 客户端通过发送 Client Hello 报文开始 SSL通信。报文中包含客户端支持的 SSL 的指定版本、加密组件(Cipher Suite)列表(所使用的加密算法及密钥长度等)。
- 服务器可进行 SSL 通信时,会以 Server Hello 报文作为应答。和客户端一样,在报文中包含 SSL 版本以及加密组件。服务器的加密组件内容是从接收到的客户端加密组件内筛选出来的。
- 之后服务器发送 Certificate 报文。报文中包含公开密钥证书。
- 最后服务器发 送Server Hello Done 报文通知客户端,最初阶段的 SSL 握手协商部分结束。
- SSL 第一次握手结束之后,客户端以 Client Key Exchange 报文作为回应。报文中包含通信加密中使用的一种被称为 Pre-master secret 的随机密码串。该报文已用步骤3中的公开密钥进行加密。
- 接着客户端继续发送 Change Cipher Spec 报文。该报文会提示服务器,在此报文之后的通信会采用 Pre-master secret 密钥加密。
- 客户端发送 Finished 报文。该报文包含连接至今全部报文的整体校验值。这次握手协商是否能够成功,要以服务器是否能够正确解密该报文作为判定标准。
- 服务器同样发送 Change Cipher Spec 报文。
- 服务器同样发送 Finished 。
- 服务器和客户端的 Finished 报文交换完毕之后, SSL 连接就算建立成功。当然,通信会收到 SSL 的保护。从此处开始进行应用层协议的通信,即发送 HTTP 请求。
- 应用层协议通信,即发送 HTTP 响应。
- 最后由客户端断开连接。断开连接时,发送 close_notify 报文。这步之后再发送 TCP FIN 报文来关闭与 TCP 的通信。
加密过程:
- 浏览器将自己支持的一套加密规则发送给网站。
- 网站从中选出一组加密算法与 HASH 算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
- 获得网站证书之后浏览器要做以下工作:
- 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信任的提示。
- 如果证书受信任,或者是用户接受了不受信任的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
- 使用约定好的 HASH 计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
- 网站接收浏览器发来的数据之后要做以下的操作:
- 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手信息,并验证 HASH 是否与浏览器发来的一致。
- 使用密码加密一段握手信息,发送给浏览器。
- 浏览器解密并计算握手信息的 HASH ,如果与服务端发来的 HASH 一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。
内容协商
- Proactive 主动式内容协商
- 指由客户端先在请求头部中提出需要的表述形式,而服务器根据这些请求头部提供特定的 representation 表述。
- Reactive 响应式内容协商
- 指服务器返回 300 Multiple Choices 或者 406 Not Acceptable,由客户端选择一种表述 URI 使用。
参考文章: