HTTP协议版本的发展(HTTP/0.9、1.0、1.1、2、3)
目录
HTTP协议层次图
HTTP/0.9
例子
HTTP/1.0
Content-Type 字段
Content-Encoding 字段
例子
1.0版本存在的问题:短链接、队头阻塞
HTTP/1.1
Host字段
Content-Length 字段
分块传输编码
1.1版本存在的问题
HTTP/2
HTTP/2数据传输
2版本存在的问题
HTTP/3
队头阻塞问题总结
HTTP1.1断点续传
如果服务器支持断点续传
如果服务器不支持断点续传
断点续传的应用
HTTP Keep-Alive 机制
HTTP协议层次图
HTTP/0.9
例子
GET /index.html
响应:
<html><body>Hello World</body>
</html>
HTTP/1.0
- 增加POST、HEAD等请求方法。
- 引入头部信息。除了要传输的数据之外,每次通信都包含头信息,用来描述一些信息。
- 传输数据格式不再局限于HTML格式。根据Content-Type可以支持多种数据格式,不仅可以用来传输文字,还可以传输图像、音频、视频等二进制文件。
- 引入cache缓存机制。
- 引入状态码。
Content-Type 字段
关于字符的编码,1.0版规定,头信息必须是 ASCII 码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式,这就是Content-Type字段的作用。
下面是一些常见的Content-Type字段的值。
text/plain
text/html
text/css
image/jpeg
image/png
image/svg+xml
audio/mp4
video/mp4
application/javascript
application/pdf
application/zip
application/atom+xml
Content-Encoding 字段
由于发送的数据可以是任何格式,因此可以把数据压缩后再发送。Content-Encoding字段说明数据的压缩方法。
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
客户端在请求时,用Accept-Encoding字段说明自己可以接受哪些压缩方法:
Accept-Encoding: gzip, deflate
例子
请求:
GET / HTTP/1.0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*
客户端请求的时候,可以使用Accept字段声明自己可以接受哪些数据格式。Accept: */*:表明户端可以接受任何格式的数据。
响应:
HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84
<html><body>Hello World</body>
</html>
1.0版本存在的问题:短链接、队头阻塞
- 短链接:每个TCP连接只能发送一个请求,当服务器响应后就会关闭这个TCP连接,下一次请求需要再次建立TCP连接。每次建立、释放TCP连接需要三次握手四次挥手,所以当网页需要发送多个HTTP请求时,就会造成性能问题。
- 队头阻塞:下一个请求必须在前一个请求响应到达之前才能发送,若前一个请求响应一直不到达,那下一个请求就不发送,同样后面的请求也被阻塞了。
HTTP/1.1
- 新增了请求方式PUT、PATCH、OPTIONS、DELETE等。
- 增强缓存策略:如Etag、If-Unmodified-Since、If-Match、If-None-Match等可供选择的了缓存头来控制缓存策略。
- 客户端请求的头信息新增了Host字段,用来指定服务器的域名。
- 引入持久连接(即长连接) :可以通过设置connection: keep-alive来保持HTTP连接不断开,避免频繁的建立、释放连接。如果客户端和服务器端发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求是,发送connection: false来明确要求服务器关闭TCP连接。(1.1默认有长链接,1.0需要手动的添加connection: keep-alive才能保持长链接)
- 加入管道机制:基于HTTP/1.1的长链接,在同一个TCP连接里,允许多个请求同时发送。但是还是有问题:假设客户端需要请求两个资源。以前的做法是,在同一个TCP连接里,先发送A请求,然后等待服务做出响应,收到后再发出B请求。管道机制允许浏览器同时发出A请求和B请求,但服务器还是按照请求顺序,先响应A请求,再响应B请求,所以这并不是真正意义上的并行传输。现阶段的浏览器厂商实现并行传输,采用的做法是允许打开多个TCP会话,这就是我们所熟悉的浏览器对同一个域下能够并行加载6~8个资源的限制。(所以说HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞)
- HTTP/1.1支持文件断点续传:RANGE:bytes,HTTP/1.0每次传送文件都是从文件头开始,即0字节处开始。RANGE:bytes=XXXX表示要求服务器从文件XXXX字节处开始传送,断点续传。返回码是206(Partial Content)
- Content-length字段:声明本次回应的数据长度。
- 分块传输编码:使用Content-Length字段的前提条件是服务器发送响应之前,必须知道响应的数据长度。对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成才能发送数据,显然这样的效率不高。更好的处理方法是,产生一块数据,就发送一块,采用“流模式”(stream),取代“缓存模式”(buffer)。因此,1.1版本规定可以不使用Content-Length字段,而使用“分块传输编码”,只要请求或回应的头信息有Transfer-Encoding字段,就表明响应将由数量未定的数据块组成。
Host字段
- HTTP/1.0 的问题:在 HTTP/1.0 中,请求只包含路径(如 GET /index.html),不包含域名信息。如果一台服务器托管了 example.com 和 test.com,服务器无法区分请求是发给哪个域名的。
- HTTP/1.1 的解决方案:强制要求客户端在请求头中添加 Host 字段,明确指定目标域名:服务器会根据 Host 字段将请求路由到对应的网站。如下:
GET /index.html HTTP/1.1
Host: example.com
Content-Length 字段
一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的。这就是Content-length字段的作用,声明本次回应的数据长度。
Content-Length: 3495
上面代码告诉浏览器,本次回应的长度是3495个字节,后面的字节就属于下一个回应了。
在1.0版中,Content-Length字段不是必需的,因为浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了
分块传输编码
Transfer-Encoding: chunked // 分块传输
每一个非空的数据块之前,会有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完毕:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked25
This is the data in the first chunk1C
and this is the second one3
con8
sequence0
1.1版本存在的问题
HTTP/2
- 二进制分帧:HTTP/1.1版本的头信息必须是文本(ASCII编码),数据体可以是文本,也可以是二进制,HTTP/2则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为“帧”(frame):头信息帧和数据帧。如下图:
- 多路复用:HTTP/2版本会复用TCP连接。在一个连接里,客户端和服务器端都可以同时发送多个请求或响应,而且不用按照顺序一一对应,这样就避免了“队头阻塞”。(多路复用是通过 流Streams和 二进制帧Frames实现的,而非直接复用原始 HTTP 消息)
- 数据流:因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。
HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。1.1版取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。
- 头信息压缩:HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如Cookie和User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
- 服务器推送:HTTP/2允许服务器不需要客户端的请求就可以主动向客户端发送资源。
常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。
- 流量控制: HTTP/2不同于HTTP/1.1的只要客户端可以处理,服务端就会尽可能快地发送数据。HTTP/2提供了客户端调整传输速度的能力(服务端也可以),在每一个数据帧中告诉对方,发送方想要接受多少字节的数据。
HTTP/2数据传输

从图中可见,所有的HTTP/2通信都在一个TCP连接上完成,这个连接可以承载任意数量的双向数据流。
每个数据流以消息的形式发送,而消息由一个或多个帧组成。这些帧可以乱序发送,然后再根据每个帧头部的stream id标识符来重新组装。
举个例子,每个请求是一个数据流,数据流以消息的方式发送,而消息又分为多个帧,帧头部记录着stream id用来标识当前帧所属的数据流,不同属的帧可以在连接中混杂在一起。接收方可以根据stream id将帧再归属到各自不同的请求中去。
另外,多路复用(连接共享)可能会导致关键请求被阻塞。HTTP/2里每个数据流都可以设置优先级和依赖,优先级高的数据流会被服务器优先处理和返回给客户端,数据流还可以依赖其他的子数据流。
2版本存在的问题
队头阻塞:
HTTP/2 的多路复用依赖于一条 TCP 连接,而 TCP 是面向字节流的协议,要求数据按顺序到达,如果某个TCP 包丢失,那么整个连接会等待重传,导致所有流被阻塞。
HTTP/3
- TCP 队头阻塞:HTTP/2 的 多路复用 在 TCP 上运行时,若一个包丢失,所有流(Stream)都会被阻塞。HTTP/3 使用 QUIC(基于 UDP),每个流独立传输,丢包只影响当前流,其他流不受影响。
HTTP/3 只使用一个 UDP 连接,但在这个连接内部通过 QUIC 协议 实现了多路复用,而不是建立多个 UDP 连接。在单个 UDP 连接内创建多个Streams流,每个 HTTP 请求/响应对应一个独立的 QUIC 流(使用Stream ID标识),这些流共享同一个 UDP 连接,但彼此独立,所以一个流丢包不会阻塞其他流传输消息。
- 减少握手延迟HTTP/2 需要 TCP 三次握手 + TLS 握手(1~2 RTT),而 HTTP/3 的 QUIC 直接内置 TLS 1.3,支持:
- 1-RTT 首次连接(比 HTTP/2 更快)。
- 0-RTT 恢复连接(对之前访问过的服务器可立即发送数据)。

队头阻塞问题总结
- 持久连接(Keep-Alive):默认复用同一个 TCP 连接,避免了频繁建立连接的开销。
- 管道化(Pipelining):允许客户端在未收到响应时发送多个请求(理论上的优化)。(响应必须按请求顺序返回:如果第一个请求的响应延迟(例如服务器处理慢或网络延迟),后续已发送的请求响应会被阻塞,即使它们已经准备就绪。)
- HTTP/2:通过多路复用(Multiplexing)在单个连接上并行传输多个请求/响应,彻底解决了应用层的队头阻塞。但若底层 TCP 丢包,仍会因重传导致所有流阻塞(TCP 层队头阻塞)。
- HTTP/3:基于 QUIC 协议(UDP 实现),解决了 TCP 层的队头阻塞,每个流独立传输,丢包不影响其他流。
HTTP1.1断点续传
如果服务器支持断点续传
客户端发起带 Range 头的请求:客户端在请求中指定需要下载的字节范围(如从第 1000 字节开始)。
GET /large-file.zip HTTP/1.1
Host: example.com
Range: bytes=1000-2000
- Range: bytes=start-end:请求文件的特定字节范围(end 可省略,表示直到文件末尾)。
- 例如:
- Range: bytes=0-499 → 请求前 500 字节
- Range: bytes=500- → 请求从第 500 字节到文件末尾
- Range: bytes=500-999,2000-2499 → 请求多个范围(较少使用)
如果服务器支持断点续传,会返回 206 状态码,并标明返回的数据范围:
HTTP/1.1 206 Partial Content
Content-Range: bytes 1000-2000/5000
Content-Length: 1001
Content-Type: application/zip[文件数据...]
- Content-Range: bytes start-end/total:
- start-end:实际返回的字节范围
- total:文件的完整大小(如果未知,可以用 * 代替)
- Content-Length:返回的数据长度(这里是 2000 - 1000 + 1 = 1001 字节)
客户端继续下载剩余部分:客户端可以继续发送新的 Range 请求,直到文件下载完成。
注意:如果客户端请求的字节范围不合法,比如超出了请求文件的大小,那么服务端就会返回416状态码。StatusRequestedRangeNotSatisfiable(请求的状态范围不满足) = 416
如果服务器不支持断点续传
如果服务器不支持 Range 请求,会直接返回 200 OK 和整个文件:
HTTP/1.1 200 OK
Content-Length: 5000
Content-Type: application/zip[完整的文件数据...]
客户端需要检查状态码是 206 还是 200 来判断是否支持断点续传。
StatusPartialContent(状态部分内容)= 206
断点续传的应用
- 大文件下载(如 ISO 镜像、视频文件):下载工具(如迅雷、IDM)可以分块下载,提高速度。如果下载中断,可以从中断的位置继续下载,而不是重新开始。
- 视频/音频流(HTTP Streaming):播放器可以只请求视频的某一段(如 Range: bytes=0-1023),实现边下边播。
- 断网恢复:浏览器或下载管理器可以记录已下载的字节位置,恢复时发送 Range 请求继续下载。
HTTP Keep-Alive 机制
- 传统HTTP(无Keep-Alive):每个HTTP请求完成后立即关闭TCP连接,下次请求需重新建立连接(三次握手)。
请求1 → 响应1 → 关闭连接
请求2 → 响应2 → 关闭连接
- 启用Keep-Alive后:多个HTTP请求复用同一个TCP连接,减少握手和挥手次数。
请求1 → 响应1 → 请求2 → 响应2 → ... → 关闭连接
- HTTP/1.1:默认启用Keep-Alive(无需显式声明),除非请求头包含 Connection: close。
- HTTP/1.0:需显式在请求头中添加 Connection: keep-alive。
GET /index.html HTTP/1.1
Host: example.com
Connection: keep-alive # 显式启用(HTTP/1.0需此字段,HTTP/1.1可省略)
参考:
HTTP 协议的历史演变和设计思路
稀土掘金:HTTP协议各个版本之间的区别