基础篇
HTTP的前世今生
HTTP
协议始于三十年前蒂姆·伯纳斯 - 李的一篇论文HTTP/0.9
是个简单的文本协议,只能获取文本资源;HTTP/1.0
确立了大部分现在使用的技术,但它不是正式标准;HTTP/1.1
是目前互联网上使用最广泛的协议,功能也非常完善;HTTP/2
基于 Google 的SPDY
协议,注重性能改善,但还未普及;HTTP/3
基于 Google 的QUIC
协议,是将来的发展方向

HTTP是什么
HTTP
是一个用在计算机世界里的协议,它确认了一种计算机之间交流通信的规范,以及相关的各种控制和错误处理方式HTTP
专门用来在两点之间传输数据,不能用于广播,寻址,路由。HTTP
传输的是文字,图片,音频,视频等超文本数据。HTTP
是构建互联网的重要基础技术,它没有实体,依赖许多其他的技术来实现,但是同时许多技术也都依赖于它。


http世界全览

- 互联网上绝大部分资源都使用
HTTP
协议传输; - 浏览器是
HTTP
协议中的请求方,即User Agent
; - 服务器是
HTTP
协议中的应答方,常用的有Apache
和Nginx
; CDN
位于浏览器和服务器之间,主要起到缓存加速的作用- 爬虫是另一类的
User Agent
,是自动访问网络资源的程序; TCP/IP
是世界上最常用的协议,HTTP
通常运行在TCP/IP
提供的可靠传输基础上DNS
域名是IP地址
的等价替代,需要用域名解析实现到IP地址的映射;URI
是用来标记互联网上资源的一个名字,由协议名+主机名+路径
构成,俗称URL
;HTTPS
相当于HTTP+SSL/TLS+TCP/IP
,为HTTP
套一个安全的外壳;- 代理是
HTTP
传输过程中的中转站,可以实现缓存加速,负载均衡等功能


HTTP分层

- 第一层:物理层,
TCP/IP
里无对应; - 第二层:数据链路层,对应
TCP/IP
的连接层; - 第三层:网络层,对应
TCP/IP
的网际层; - 第四层:传输层,对应
TCP/IP
的传输层; - 第五,六,七层:统一对应到
TCP/IP
的应用层;
OSI七层模型(理论参考模型)与TCP/IP四层模型(实际互联网应用模型),再结合 "分层协作" 的逻辑,看 HTTP 如何依赖底层协议实现 "客户端 - 服务器" 的通信,以及它自身在分层中的角色。
一、先理清基础:OSI 七层模型与 TCP/IP 四层模型的对应关系
有一个辨别四层和七层比较好的(但不是绝对的)小窍门,"两个凡是":凡是由操作系统负责处理的就是四层或四层以下,否则,凡是需要由应用程序(也就是你自己写代码)负责处理的就是七层
两种模型的核心是 "上层依赖下层提供的服务,下层为上层屏蔽实现细节"(比如 HTTP 不用关心数据如何通过网线传输,只需专注 "请求 / 响应" 的逻辑)。具体对应如下:
OSI 七层模型(从下到上) | 核心功能 | TCP/IP 四层模型(从下到上) | 核心功能 | 与 HTTP 的关联 |
---|---|---|---|---|
1. 物理层(Physical) | 传输 "比特流"(0/1 电信号),如网线、无线信号 | 无直接对应(融入链路层) | 物理介质的信号传输 | HTTP 不直接交互,依赖其传输底层数据 |
2. 数据链路层(Data Link) | 将比特流封装成 "帧",处理 MAC 地址、差错校验(如以太网、Wi-Fi) | 1. 链路层(Link) | 同 OSI 数据链路层,负责局域网内传输 | HTTP 不直接交互,依赖其封装帧数据 |
3. 网络层(Network) | 封装 "数据包",处理 IP 地址、路由(找目标服务器) | 2. 网际层(Internet) | 同 OSI 网络层,负责跨网络路由 | HTTP 依赖其实现 "找服务器"(IP 寻址) |
4. 传输层(Transport) | 封装 "段 / 报",处理端口、可靠传输(如 TCP/UDP) | 3. 传输层(Transport) | 同 OSI 传输层,负责端到端通信 | HTTP 依赖其实现 "可靠连接"(TCP) |
5. 会话层(Session) | 建立 / 维护 / 断开 "会话"(如 TCP 连接管理) | 4. 应用层(Application) | 融合 OSI 5-7 层功能,负责具体应用逻辑 | HTTP 的会话管理(如连接复用)依赖此层逻辑 |
6. 表示层(Presentation) | 处理数据格式(编码、压缩、加密),如 JSON、gzip、TLS | 4. 应用层(Application) | 同表示层功能,由应用协议自行实现 | HTTP 的编码、压缩、HTTPS 加密在此层处理 |
7. 应用层(Application) | 定义 "用户级" 通信协议(如 HTTP、FTP、DNS) |
二、HTTP 的分层定位:属于 "应用层",依赖底层协议实现通信
HTTP 的本质是应用层协议------ 它不关心 "数据如何从 A 地传到 B 地"(底层的物理层、链路层、网络层、传输层负责),只关心 "数据传什么、怎么传(请求 / 响应格式)、传完后怎么处理"。
可以用 "快递运输类比" 理解 HTTP 与各层的关系:
- 你(客户端)要寄一个包裹(HTTP 请求)给朋友(服务器);
- HTTP(应用层):你写的 "快递单"(请求行、请求头、请求体),明确包裹内容(如 "商品数据")、收件人(服务器域名)、寄件要求(如 "优先送达");
- TCP(传输层):快递员,负责保证包裹不丢、不损坏(可靠传输),并记录 "收件人电话(端口号,如 HTTP 默认 80)";
- IP(网际层):快递路线规划师,根据 "收件人地址(服务器 IP)" 规划从你家到朋友家的路线(跨网络路由);
- 链路层 / 物理层:快递车、公路 / 铁路,负责将包裹从一个站点运到下一个站点(物理传输)。
三、关键:HTTP 如何与各层协作?(以 "发送 HTTP 请求" 为例拆解)
HTTP 的通信过程,本质是 "数据在各层层层封装、传输、再层层解封装" 的过程,每一层都为数据添加 "自己的标识"(头部信息),帮助数据准确到达目标并被处理。
步骤 1:应用层(HTTP)------ 封装 "请求报文"
客户端(如浏览器)根据用户操作(如访问www.baidu.com
),按 HTTP 协议格式生成 "HTTP 请求报文":
yaml
GET / HTTP/1.1 # 请求行(方法、路径、版本)
Host: www.baidu.com # 请求头(服务器域名、缓存策略等)
User-Agent: Chrome/118.0.0.0 # 客户端标识
Accept: text/html,application/xhtml+xml # 可接收的响应格式
(空行,分隔请求头和请求体)
(请求体,GET请求无内容)
这一步是 HTTP 的核心工作:定义 "要什么资源、用什么规则要"。
步骤 2:传输层(TCP)------ 封装 "TCP 段",建立可靠连接
- 首先,TCP 会先与服务器的 80 端口(HTTP 默认端口)建立 "三次握手",确保连接可靠(见之前 HTTP 工作原理中的 TCP 握手);
- 然后,TCP 将 HTTP 请求报文(应用层数据)封装成 "TCP 段",添加TCP 头部(包含:源端口(客户端随机端口,如 54321)、目标端口(80)、序列号(确保数据按顺序到达)、确认号(确保数据不丢失));
- 作用:TCP 为 HTTP 提供 "可靠传输保障"------ 如果数据丢包,TCP 会重传;如果数据乱序,TCP 会排序。
步骤 3:网际层(IP)------ 封装 "IP 数据包",实现路由
IP 层将 TCP 段(传输层数据)封装成 "IP 数据包",添加IP 头部(包含:源 IP(客户端 IP,如 192.168.1.100)、目标 IP(服务器 IP,如 14.215.177.38)、TTL(数据存活时间,避免循环传输));
- 作用:IP 负责 "找路"------ 通过路由器将 IP 数据包从客户端所在的局域网,路由到服务器所在的网络(比如从你家的路由器,经过运营商机房、骨干网,最终到达百度的服务器集群)。
步骤 4:链路层(Data Link)------ 封装 "数据帧",实现局域网传输
链路层将 IP 数据包封装成 "数据帧",添加帧头部(包含:源 MAC 地址(客户端网卡 MAC)、目标 MAC 地址(下一跳设备的 MAC,如你家路由器的 MAC)、帧校验序列(检查数据是否损坏));
- 作用:负责 "局域网内传输"------ 比如将数据帧从你的电脑(客户端)通过网线 / Wi-Fi 传到路由器,再由路由器转发到下一个设备(如运营商的交换机)。
步骤 5:物理层(Physical)------ 传输 "比特流"
链路层的 "数据帧" 最终会被转换为 "比特流"(0 和 1 的电信号 / 光信号),通过物理介质(网线、光纤、无线信号)传输到下一个设备;
- 作用:最底层的 "信号传输",HTTP 完全不感知这一层的存在。
步骤 6:服务器端 ------ 层层解封装,最终交给 HTTP 处理
服务器接收数据后,按 "从下到上" 的顺序层层解封装:
- 物理层:将比特流还原为数据帧;
- 链路层:验证帧校验,去除帧头部,得到 IP 数据包;
- 网际层:验证 IP 头部,去除 IP 头部,得到 TCP 段;
- 传输层:验证 TCP 头部,去除 TCP 段头部,得到 HTTP 请求报文;
- 应用层:将 HTTP 请求报文交给 Web 服务器(如 Nginx),Web 服务器解析请求(如 "要访问根路径 /"),处理后生成 HTTP 响应报文;
- 响应报文再按 "应用层→传输层→网际层→链路层→物理层" 的顺序封装,传回客户端,客户端解封装后,浏览器渲染响应内容(如百度首页)。
四、补充:OSI 5-7 层功能在 HTTP 中的体现(为什么 TCP/IP 应用层要融合这三层?)
你提到 OSI 的会话层(5)、表示层(6)、应用层(7)统一对应 TCP/IP 的应用层,这是因为 TCP/IP 模型更侧重 "实际应用",将 "会话管理、数据表示" 的功能交给应用协议(如 HTTP)和传输层(如 TCP)共同实现,而非单独分层。具体到 HTTP:
1. 会话层(Session)功能:由 HTTP+TCP 共同实现
会话层的核心是 "建立、维护、断开通信会话",HTTP 通过以下方式实现:
- 会话建立:TCP 三次握手建立连接,HTTP 在连接上发送请求;
- 会话维护 :HTTP/1.1 的
Keep-Alive
(持久连接),让一个 TCP 连接可处理多个 HTTP 请求,避免频繁建立会话; - 会话断开 :TCP 四次挥手断开连接,或 HTTP 主动发送
Connection: close
请求关闭连接。
2. 表示层(Presentation)功能:由 HTTP 直接实现
表示层的核心是 "处理数据格式、编码、压缩、加密",HTTP 通过请求头 / 响应头明确这些规则:
- 数据格式 :
Content-Type
头(如text/html
表示 HTML 文本、application/json
表示 JSON 数据、image/jpeg
表示 JPEG 图片); - 数据压缩 :
Content-Encoding
头(如gzip
表示数据用 gzip 压缩,客户端需解压后处理); - 数据加密 :HTTPS(HTTP+TLS),TLS 属于表示层的加密功能,通过
https://
协议标识,确保数据传输安全; - 字符编码 :
Content-Charset
头(如UTF-8
,确保客户端正确解析中文等特殊字符)。
3. 应用层(Application)功能:HTTP 的核心职责
定义 "用户级" 的通信规则,即 "请求什么、怎么响应":
- 请求方法(GET/POST/PUT/DELETE)定义 "操作意图";
- 状态码(200/404/500)定义 "处理结果";
- 报文格式(请求行 + 请求头 + 请求体、响应行 + 响应头 + 响应体)定义 "数据结构"。
五、总结:HTTP 分层的核心意义
- 解耦与模块化:各层各司其职,HTTP 只需专注 "应用逻辑",底层的 "传输、路由、物理传输" 由其他协议负责 ------ 比如想提升安全性,只需在表示层添加 TLS(即 HTTPS),无需修改 TCP/IP 底层;想提升传输效率,只需升级 HTTP 版本(如 HTTP/2→HTTP/3),底层仍可复用 TCP/IP。
- 可扩展性:新的应用协议(如 HTTP/3)可基于现有底层协议(如 QUIC 替代 TCP,仍用 IP 路由、链路层传输)快速落地,无需重构整个网络模型。
- 故障定位清晰:若 HTTP 请求失败,可按分层排查 ------ 比如 "无法连接" 可能是 TCP 层(端口错误)或网际层(IP 错误)问题;"响应 404" 是应用层(HTTP 路径错误)问题。
简言之,HTTP 是 "应用层的协议",它像一个 "指挥官",依赖传输层(TCP)的 "可靠运输"、网际层(IP)的 "路线规划"、链路层 / 物理层的 "硬件传输",最终实现 "客户端与服务器的资源交互"。理解 HTTP 的分层,就能理解 "为什么 HTTP 需要 TCP""为什么访问网站需要 IP""HTTPS 为什么更安全" 等核心问题。

键入网址到回车发生什么
HTT
P协议基于底层的TCP/IP
协议,所以必须要用IP地址
建立连接- 如果不知道
IP地址
,就要用DNS协议
去解析得到IP地址
,否则会连接失败 - 建立
TCP
连接后会顺序收发数据,请求方和应答方都必须依据HTTP
规范构建和解析报文 - 为了减少响应事件,整个过程中的每一个环节都会有缓存,能够实现短路操作
- 虽然现实中的
HTTP
传输过程非常复杂,但是理论上仍然可以简化成实验里的两点模型
HTTP报文结构是什么样的?
报文结构
- 你也许对TCP/UDP的报文格式有所了解,拿TCP报文来举例,它在实际传输的数据之前附加了一个20字节的头部数据,存储TCP协议必须的额外信息,例如发送方的端口号,接收方的端口号,包序号,标志位等等。
- 有了这个附加的TCP头,数据包才能够正确传输,到了目的地后把头部去掉,就可以拿到真正的数据。

HTTP协议也是与TCP/UDP类似,同样也需要在实际传输的数据前附加一些头数据,不过与TCP/UDP不同的是,它是一个纯文本的协议,所以头数据都是ASCII码的文本,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂
HTTP
协议的请求报文和响应报文的结构基本相同,由三大部分组成
起始行(start line)
:描述请求或者响应的基本信息;头部字段集合(header)
:使用key-value形式更详细地说明报文;消息正文(entity)
:实际传输的数据,它不一定是纯文本,可以是图片,视频等二进制数据;
这其中前两部分起始行
和头部字段
经常又合称为请求头
或者响应头
,消息正文
又称为实体
,但与header
对应,很多时候就直接称为body
。
HTTP协议
规定报文必须有header
,但是可以没有body
,而且在header
之后必须要有一个空行,也就是CRLF
,十六进制的0D0A
。
CRLF(Carriage Return Line Feed,回车换行符) 是由两个 ASCII 控制字符组合而成的 "文本换行标识",也是网络协议(如 HTTP、SMTP)中定义 "数据边界" 的核心分隔符。它的本质是通过两个字符的协作,完成 "光标回到行首 + 下移一行" 的完整换行动作,同时在网络通信中明确 "不同数据段的分割规则"。
CRLF 的本质:两个字符的分工与起源
CRLF 由两个独立的 ASCII 字符组成,各自对应早期计算机终端(如电传打字机、老式终端)的物理操作逻辑,这也是 "为什么需要两个字符" 的历史根源:
字符 | 名称 | ASCII 码 | 原始物理含义(电传打字机时代) | 现代文本中的作用 |
---|---|---|---|---|
\r |
Carriage Return(CR,回车符) | 13 | 让 "打印头" 从当前行的右侧回到左侧(行首) | 使光标移动到当前行的开头位置 |
\n |
Line Feed(LF,换行符) | 10 | 让 "纸卷" 向下滚动一行(但打印头位置不变) | 使光标从当前行向下移动一行 |
早期电传打字机需要先 "回车"(CR)让打印头归位,再 "换行"(LF)让纸卷下移,才能正确开始下一行打印 ------ 这一逻辑被延续到计算机文本和网络协议中,形成了 "CRLF" 组合。
所以一个完整的HTTP报文
就像是下图的这个样子,注意在header
和body
之间有一个空行。

说到这里,我不由得想起了一部老动画片《大头儿子和小头爸爸》,你看,HTTP 的报文结构像不像里面的"大头儿子"?
报文里的 header 就是"大头儿子"的"大头",空行就是他的"脖子",而后面的 body 部分就是他的身体了。
看一下我们之前用 Wireshark 抓的包吧。

在这个浏览器发出的请求报文里,第一行"GET / HTTP/1.1"就是请求行
,而后面的"Host""Connection"等等都属于 header
,报文的最后是一个空白行结束,没有 body。
在很多时候,特别是浏览器发送 GET 请求的时候都是这样,HTTP 报文经常是只有 header
而没 body
,相当于只发了一个超级"大头"过来,你可以想象的出来:每时每刻网络上都会有数不清的"大头儿子"在跑来跑去。
不过这个"大头"也不能太大,虽然 HTTP 协议对 header 的大小没有做限制,但各个 Web 服务器都不允许过大的请求头,因为头部太大可能会占用大量的服务器资源,影响运行效率。
请求行
了解了HTTP报文的基本结构后,我们来看看请求报文里的起始行也就是请求行(request line),它简明地描述了客户端想要如何操作服务器端的资源。 请求行由三部分组成
- 请求方法:是一个动词,如GET/POST,表示对资源的操作;
- 请求目标:通常是一个URI,标记了请求方法要操作的资源;
- 版本号:表示报文使用的HTTP协议版本;
这三个部分通常由空格来分隔,最后要用CRLF换行表示结束。

状态行
比起请求行来说,状态行要简单一些,同样也是由三部分构成:
- 版本号:表示报文使用的HTTP协议版本;
- 状态码:一个三位数,用代码的形式表示处理的结果,比如200是成功,500是服务器错误;
- 原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因

头部字段
请求行或状态行再加上头部字段集合就构成了HTTP报文里完整的请求头或响应头


请求头和响应头的结构是基本一样的,唯一的区别是起始行,所以我们把请求头和响应头里的字段放在一起介绍。
头部字段是key-value的形式,key和value之间使用" :"分割,最后用CRLF换行表示字段结束。比如在"Hosting:127.0.0.1"这一行里key就是Host,value就是127.0.0.1
HTTP头字段非常灵活,不仅可以使用标准里的Host,Connection等已有头,也可以添加自定义头,这就给HTTP协议带来了无限的扩展可能。 不过使用头字段需要注意下面几点
:
- 字段名不区分大小写
- 字段名不能出现空格,可以使用连字符"-",但是不能使用下划线"_"
- 字段名后面必须紧接着冒号:,不能有空格,而冒号后面的字段值前可以有多个空格
- 字段的顺序是没有意义的,可以任意排列不影响语义
- 字段原则上不能重复,除非这个字段本身的语义允许

标准请求方法
目前
HTTP/1.1
规定了八种方法,单词都必须是大写的形式
-
GET
:获取资源,可以理解为读取或者下载数据; -
HEAD
:获取资源的元信息; -
POST
:向资源提交数据,相当于写入或上传数据; -
PUT
:类似POST
; -
DELETE
:删除资源; -
CONNECT
:建立特殊的连接隧道; -
OPTIONS
:列出可对资源实行的方法; -
TRACE
:追踪请求 - 响应的传输路径 -
这些方法是客户端发出的,要求服务器执行的,对资源的一种操作
-
请求方法是对服务器的指示,真正应该如何处理由服务器来决定
-
最常用的请求方法是
GET
和POST
,分别是获取数据和发送数据 -
HEAD
方法是轻量级的GET
,用来获取资源的元信息 -
PUT
基本上是POST
的同义词,多用于更新数据 -
安全
与幂等
是描述请求方法的两个重要属性,具有理论指导意义,可以帮助我们设计系统
安全与幂等: 在HTTP协议中,安全与幂等是描述请求方法特性的两个核心属性,它们并非强制约束,但是作为设计规范,能指导我们构建更加可靠,可预测,易维护的系统。理解这两个属性,本质是理解请求对服务器资源状态的影响规律,从而在接口设计,重试机制,缓存策略等场景中做出合理决策。
安全的请求方法是指: 不会修改服务器上面的内容,仅用于获取资源。即这类请求执行后,服务器的资源不会发生任何变化。
GET
:最典型的安全方法,用于获取资源,执行后服务器资源状态不变(仅返回数据,不增删改查)HEAD
:安全方法,与GET
类似,但是仅返回响应头(不返回响应体),同样不修改资源非安全方法
:POST
,PUT
,DELETE
,这些会改变服务器资源的状态
安全属性的设计意义
缓存可行性
:安全方法的响应可以被浏览器,CDN
等缓存(因为资源不会被请求本身修改)。例如,GET
请求的结果会被浏览器缓存,下次相同请求可直接使用本地缓存,减少服务器压力。用户信任
:用户可以放心执行安全方法,无需担心"重复操作导致意外的后果"。爬虫与索引
:搜索爬虫主要依赖GET
方法抓取内容,正是因为GET
是安全的,不会破坏网站数据。
幂等的请求方法是指:多次执行相同的请求,最终服务器资源状态产生的影响与执行一次完全相同。即重复调用不会导致意外的状态变化,但是允许过程不同(比如第一次执行是创建,后续执行是忽略,但是最终状态一致) 幂等的核心是状态结果的一致性
GET
:幂等
。多次请求同一个资源,服务器资源状态不变,结果一致。PUT
:幂等
。用于全量更新资源(如PUT /user/1 更新ID=1的用户信息),无论执行几次,最终的用户1的信息都是最后一次请求中的数据。DELETE
:幂等
。用于删除资源(如DELETE /order/100 删除ID=100的订单),第一次执行订单从存在到不存在,后续执行多少次,订单都是不存在,最终的状态一致。POST
:非幂等方法
。用于创建资源(如POST /order提交订单),多次执行可能创建多个订单(每个订单的ID不同),最终状态与执行一次不同(多了N-1个订单),因此非幂等。
幂等性的关键细节
幂等关注状态变化而非响应内容
:两次相同的GET请求之间有新的内容被创建了,两次响应的内容不同,但是GET没有改变订单的状态,响应不同是其他请求造成的。幂等不要求每次响应完全相同
:例如,DELETE/order/100 第一次返回204 No Content(删除成功),第二次返回404 Not Found(订单已不存在),响应不同,但最终状态都是订单100不存在,因此是幂等的。幂等需要请求参数完全相同
:若两次PUT请求的参数不相同,则不满足相同请求,结果自然不同,这与幂等性无关。
幂等属性的设计意义
重试机制的可靠性
:网络不稳定的时候(超时),客户端需要重试请求。幂等方法可安全重试------------例如,支付系统中,用户点击支付后请求超时,客户端重试PUT /pay/100(幂等),不会导致重复扣款;而若使用POST(非幂等),重试可能导致重复支付。分布式系统的一致性
:在分布式场景中(如微服务调用),请求可能被重复投递(如消息队列重试),幂等设计可以确保重复投递的请求不会破坏数据的一致性(如多次修改库存的请求,最终库存只减少一次)。简化并发处理
:幂等方法无需担心并发重复执行的副作用,例如,多个线程同时执行DELETE /order/100,最终结果仍是订单被删除,无需复杂的锁机制避免重复操作。
安全与幂等的关系
安全和幂等是两个独立的属性,两者既可能重叠,也可能独立存在,核心区别在于:
- 安全:关注 "是否修改资源"(副作用有无);
- 幂等:关注 "多次修改的结果是否一致"(副作用的重复性)。
两者的关系可用表格清晰展示(以 HTTP 方法为例):
HTTP 方法 | 是否安全(Safe) | 是否幂等(Idempotent) | 核心原因 |
---|---|---|---|
GET | 是 | 是 | 仅获取资源(不修改),多次执行资源状态不变 |
HEAD | 是 | 是 | 同 GET(仅返回头),不修改资源,多次执行结果一致 |
PUT | 否 | 是 | 会修改资源(不安全),但多次执行最终状态相同(幂等) |
DELETE | 否 | 是 | 会删除资源(不安全),但多次执行最终状态相同(幂等) |
POST | 否 | 否 | 会创建资源(不安全),多次执行可能创建多个资源(非幂等) |
如何使用这个两个属性设计系统
-
根据业务场景选择合适的 HTTP 方法:
- 若需 "查询数据":用 GET(安全 + 幂等),可缓存,用户可放心刷新;
- 若需 "创建数据":用 POST(非安全 + 非幂等),需做好防重复提交(如添加唯一订单号);
- 若需 "更新数据":用 PUT(非安全 + 幂等),支持重试,适合全量更新;
- 若需 "删除数据":用 DELETE(非安全 + 幂等),重试不会导致错误。
-
非幂等场景必须做 "防重复" 处理 :对于 POST 等非幂等方法(如提交订单、支付),需通过 "唯一标识"(如订单号、请求 ID)实现幂等效果 ------ 例如,客户端生成 UUID 作为
request_id
,服务器首次处理时记录 ID,后续相同 ID 的请求直接返回 "已处理",避免重复创建资源。 -
安全方法可大胆缓存:对 GET/HEAD 请求,可在服务器、CDN、浏览器中多层缓存,减少重复计算和网络传输(如商品详情页、静态资源),但需注意设置合理的缓存过期时间(避免返回旧数据)。
-
幂等方法简化重试逻辑:对 PUT/DELETE 请求,客户端可实现自动重试(如超时后重试 3 次),无需担心 "重试导致数据错误",尤其适合分布式系统(如微服务调用超时、消息队列重试)。
URI

URI
是用来唯一标记服务器上资源的一个字符串,通常也称为 URL;URI
通常由scheme
、host:port
、path
和query
四个部分组成,有的可以省略;scheme
叫"方案名"或者"协议名",表示资源应该使用哪种协议来访问;- "
host:port
"表示资源所在的主机名和端口号; path
标记资源所在的位置;query
表示对资源附加的额外要求;- 在
URI
里对"@&/
"等特殊字符和汉字必须要做编码,否则服务器收到HTTP
报文后会无法正确处理

- URI 是 "资源的身份证" :用于唯一标识互联网上的任何资源(如网页、图片、文件、API 接口),标识方式可以是 "告诉资源在哪里"(URL),也可以是 "给资源起个唯一名字"(另一种 URI:URN);
- URL 是 "资源的地址" :不仅标识资源,还明确了 "如何访问资源"(如通过 HTTP 协议、在哪个服务器上、哪个路径下)。
对比维度 | URI(统一资源标识符) | URL(统一资源定位符) | 关键结论 |
---|---|---|---|
核心功能 | 唯一 "标识" 资源(回答 "资源是谁") | 唯一 "定位 + 访问" 资源(回答 "资源在哪、怎么访问") | URI 是 "标识",URL 是 "标识 + 定位 + 访问"------URL 比 URI 多了 "访问方式" 和 "位置信息" |
必须包含的信息 | 至少包含 "方案" 和 "资源标识"(如urn:isbn:9787115428028 ) |
必须包含 "方案""主机名"(位置),通常包含 "路径"(如https://www.baidu.com/s ) |
URI 可无位置信息(如 URN),URL 必须有位置信息(否则无法定位) |
实际应用场景 | 1. 用名称标识资源(如书籍 ISBN、专利号);2. 抽象描述资源(不关心访问方式) | 1. 浏览器地址栏访问网页;2. API 接口调用(如https://api.example.com/user/1 );3. 下载文件(如ftp://example.com/file.zip ) |
日常互联网使用中,99% 的场景是 URL;URN 仅在 "需要永久标识资源且不依赖位置" 的场景(如学术、专利)使用 |
URL的通用结构
组件(Component) | 含义与作用 | 示例中的对应内容 | 是否必选 | 关键说明 |
---|---|---|---|---|
方案(Scheme) | 定义访问资源的 "协议或方式",告诉客户端 "如何获取资源" | https (HTTP 协议的加密版) |
是 | URL 必须包含 Scheme(如 http、https、ftp);URN 的 Scheme 通常是urn (如urn:isbn:9787115428028 ) |
授权(Authority) | 资源所在的 "服务器信息",通常包含 "用户认证(可选)""主机名(域名 / IP)""端口(可选)" | www.baidu.com (主机名,默认端口 443) |
否(但 URL 通常包含) | 格式可扩展为 [用户:密码@]主机名[:端口] (如ftp://user:pass@ftp.example.com:21 ) |
路径(Path) | 资源在服务器上的 "具体位置",类似文件系统的目录路径 | /s (百度搜索的路径标识) |
否 | 用/ 分隔层级(如/blog/2024/01 表示 2024 年 1 月的博客目录) |
查询(Query) | 向服务器传递的 "参数",用于筛选或定制资源(如搜索关键词、分页信息) | wd=URI (搜索关键词为 "URI") |
否 | 以? 开头,多个参数用& 分隔(如?page=1&size=10 表示第 1 页,每页 10 条) |
片段(Fragment) | 资源内部的 "锚点",用于定位资源的特定部分(如网页内的某个章节) | top (定位到网页顶部) |
否 | 以# 开头,片段不会发送到服务器(仅由客户端解析,如浏览器滚动到锚点位置) |
响应状态
- 状态码在响应报文里表示了服务器对请求的处理结果;
- 状态码后的原因短语是简单的文字描述,可以自定义;
- 状态码是十进制的三位数,分为五类,从
100
到599
; 2××
类状态码表示成功,常用的有200
、204
、206
;3××
类状态码表示重定向,常用的有301
、302
、304
;4××
类状态码表示客户端错误,常用的有400
、403
、404
;5××
类状态码表示服务器错误,常用的有500
、501
、502
、503
HTTP特点

HTTP
是灵活可扩展的,可以任意添加头字段实现任意功能;HTTP
是可靠传输协议,基于TCP/IP
协议"尽量"保证数据的送达;HTTP
是应用层协议,比FTP
、SSH
等更通用功能更多,能够传输任意数据;HTTP
使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求;HTTP
本质上是无状态的,每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息

HTTP优缺点
HTTP
最大的优点是简单、灵活和易于扩展;HTTP
拥有成熟的软硬件环境,应用的非常广泛,是互联网的基础设施;HTTP
是无状态的,可以轻松实现集群化,扩展性能,但有时也需要用Cookie
技术来实现"有状态";HTTP
是明文传输,数据完全肉眼可见,能够方便地研究分析,但也容易被窃听;HTTP
是不安全的,无法验证通信双方的身份,也不能判断报文是否被窜改;HTTP
的性能不算差,但不完全适应现在的互联网,还有很大的提升空间
HTTP(Hypertext Transfer Protocol,超文本传输协议)作为互联网的核心协议,支撑了全球绝大多数的资源交互(如网页访问、API调用、文件传输等)。其设计既有适应互联网发展的"天然优势",也存在随着技术演进逐渐暴露的"局限性"。理解其优缺点,能帮助我们在实际项目中更好地利用其优势、规避风险。
一、HTTP的核心优点:奠定其成为互联网基石的原因
1. 简单性:易于理解和实现
HTTP的报文结构(请求行/响应行、请求头/响应头、请求体/响应体)设计简洁,字段语义明确(如GET
表示获取资源、200
表示成功),开发者无需复杂学习就能解析和构造HTTP报文。
- 例:一个完整的HTTP请求仅需几行文本(
GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n
),服务器和客户端的实现门槛极低,这也是它能快速普及的核心原因。
2. 灵活性:支持多种数据格式与场景
HTTP不限制传输的数据类型(通过Content-Type
头指定),可传输文本(HTML、JSON)、二进制(图片、视频)、表单数据等,能适应从静态网页到动态API、文件上传等几乎所有互联网场景。
- 例:同一个域名下,
https://example.com/page
返回HTML网页(text/html
),https://example.com/api/user
返回JSON数据(application/json
),https://example.com/photo
返回图片(image/jpeg
),协议层无需额外适配。
3. 无状态:轻量且易于扩展
HTTP是"无状态协议"------服务器不会保存客户端的历史请求状态(如"用户是否登录过""上一次访问的页面")。这一特性带来两个核心优势:
- 服务器负担轻:无需在内存中维护大量会话状态,单个服务器可支持更多并发连接;
- 分布式友好:客户端的请求可随机分配到集群中的任何服务器(无需同步状态),便于水平扩展(如电商大促时增加服务器节点)。
4. 可扩展性:通过头部机制适配新需求
HTTP的"头部(Header)"设计具有极强的可扩展性:标准头部(如Cache-Control
控制缓存、Authorization
处理认证)覆盖基础需求,自定义头部(如X-Request-ID
追踪请求、X-User-Region
标识用户地区)可满足业务个性化需求。
- 例:当需要对请求进行链路追踪时,无需修改HTTP协议本身,只需在请求头中添加
X-Trace-ID
,所有中间节点(如网关、服务)传递该字段即可实现追踪。
5. 兼容性:跨平台、跨语言的普适性
几乎所有操作系统(Windows、Linux、macOS、iOS、Android)、编程语言(Java、Python、JavaScript等)、网络设备(路由器、网关、CDN)都原生支持HTTP,形成了"全生态兼容"的局面。
- 例:无论是浏览器(Chrome、Safari)、手机APP(微信、抖音)、后端服务(Nginx、Tomcat),还是嵌入式设备(智能手表、智能家居),都能通过HTTP进行通信,无需额外适配。
二、HTTP的主要缺点:随技术发展暴露的局限性
1. 无状态的副作用:状态维护需额外机制
"无状态"虽然轻量,但在需要"连续交互"的场景(如登录后访问个人中心、购物车操作)中,必须通过额外机制(Cookie、Session、Token)维持状态,这会增加系统复杂度:
- 例:用户登录后,服务器需生成Token并通过响应头返回,客户端需存储Token(如LocalStorage),后续请求需在
Authorization
头中携带Token------这一系列操作都是对"无状态"的补充,可能引入Token泄露、过期管理等问题。
2. 安全性缺陷:明文传输易被窃听和篡改
HTTP传输的数据(包括请求头、请求体)是明文(未加密),在传输过程中(如公共Wi-Fi、运营商链路)可能被窃听(获取账号密码、支付信息)或篡改(替换商品价格、植入恶意代码)。
- 例:用户在咖啡厅通过HTTP提交登录表单,攻击者可通过抓包工具直接获取
username=xxx&password=xxx
的明文数据,导致账号被盗。 - 解决方案:需通过HTTPS(HTTP+TLS加密)弥补,但HTTPS会增加CPU加密开销和网络延迟(约10%-30%)。
3. 性能瓶颈:HTTP/1.1的队头阻塞与连接开销
HTTP/1.1(目前仍广泛使用的版本)存在两个核心性能问题:
- 队头阻塞(Head-of-Line Blocking):同一TCP连接中,多个请求需串行处理(前一个请求未完成,后一个请求必须等待),若某个请求超时或卡顿,后续所有请求都会被阻塞;
- 连接开销大 :虽然HTTP/1.1通过
Keep-Alive
支持持久连接(一个TCP连接处理多个请求),但建立TCP连接的"三次握手"仍需耗时(约100ms-1s,取决于网络),且浏览器对同一域名的并发连接数有限制(通常6-8个)。
4. 语义模糊:方法和状态码的滥用风险
HTTP定义的方法(GET/POST/PUT/DELETE)和状态码(200/404/500)有明确语义,但实际开发中常被滥用:
- 例:用GET请求修改资源(如
GET /delete?id=1
删除数据),违反GET的"安全"特性(可能被缓存或误触发);用200状态码返回所有结果(包括错误,如{"code":-1,"msg":"失败"}
),忽略了400/401等错误码的语义,导致客户端难以通过状态码快速判断请求结果。
5. 缓存机制复杂:易导致数据不一致
HTTP的缓存机制(Cache-Control
、ETag
、Last-Modified
等)虽然能减少重复请求,但配置不当易引发"缓存脏数据"问题:
- 例:若静态资源(如CSS、JS)的
Cache-Control: max-age=3600
设置过长,当资源更新后,客户端仍会使用旧缓存,导致页面样式错乱;若设置过短,又会频繁请求服务器,失去缓存意义。
三、总结:HTTP的"功与过"及演进方向
HTTP的优点(简单、灵活、可扩展、兼容性)使其成为互联网的"通用语言",支撑了从早期静态网页到现代微服务的所有场景;而其缺点(安全性、性能、状态管理)则推动了协议的持续演进:
- 安全性:通过HTTPS(HTTP+TLS)解决明文传输问题,已成为主流(浏览器对HTTP网站标记"不安全");
- 性能:HTTP/2(多路复用、服务器推送)解决队头阻塞,HTTP/3(基于QUIC协议,替换TCP为UDP)进一步降低延迟;
- 状态管理:通过JWT(JSON Web Token)等标准化Token机制,简化无状态下的身份验证。
简言之,HTTP并非"完美协议",但它的设计哲学(简单优先、按需扩展)使其能通过"补丁"(HTTPS)和"升级"(HTTP/2/3)适应互联网的发展,这正是其生命力的核心所在。
HTTP的实体数据
数据类型与编码
text
:即文本格式的可读数据,我们最熟悉的应该就是text/html
了,表示超文本文档,此外还有纯文本text/plain
、样式表text/css
等。image
:即图像文件,有image/gif
、image/jpeg
、image/png
等。audio/video
:音频和视频数据,例如audio/mpeg
、video/mp4
等。application
:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有application/json
,application/javascript
、application/pdf
等,另外,如果实在是不知道数据是什么类型,像刚才说的"黑盒",就会是application/octet-stream
,即不透明的二进制数据
但仅有
MIME type
还不够,因为HTTP
在传输时为了节约带宽,有时候还会压缩数据,为了不要让浏览器继续"猜",还需要有一个"Encoding type",告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据。
比起 MIME type
来说,Encoding type
就少了很多,常用的只有下面三种
gzip
:GNU zip
压缩格式,也是互联网上最流行的压缩格式;deflate
:zlib
(deflate
)压缩格式,流行程度仅次于gzip
;br
:一种专门为HTTP
优化的新压缩算法(Brotli
)
数据类型使用的头字段
有了 MIME type
和 Encoding type
,无论是浏览器还是服务器就都可以轻松识别出 body
的类型,也就能够正确处理数据了。
HTTP
协议为此定义了两个 Accept
请求头字段和两个 Content
实体头字段,用于客户端和服务器进行"内容协商"。也就是说,客户端用 Accept
头告诉服务器希望接收什么样的数据,而服务器用 Content
头告诉客户端实际发送了什么样的数据

Accept
字段标记的是客户端可理解的MIME
type,可以用","做分隔符列出多个类型,让服务器有更多的选择余地,例如下面的这个头:
bash
Accept: text/html,application/xml,image/webp,image/png
这就是告诉服务器:"我能够看懂 HTML、XML 的文本,还有 webp
和 png
的图片,请给我这四类格式的数据"。
相应的,服务器会在响应报文里用头字段Content-Type
告诉实体数据的真实类型:
arduino
Content-Type: text/html
Content-Type: image/png
这样浏览器看到报文里的类型是"text/html"就知道是 HTML 文件,会调用排版引擎渲染出页面,看到"image/png"就知道是一个 PNG 文件,就会在页面上显示出图像。
Accept-Encoding字段标记的是客户端支持的压缩格式,例如上面说的 gzip、deflate 等,同样也可以用","列出多个,服务器可以选择其中一种来压缩数据,实际使用的压缩格式放在响应头字段Content-Encoding
里
makefile
Accept-Encoding: gzip, deflate, br
Content-Encoding: gzip
不过这两个字段是可以省略的,如果请求报文里没有 Accept-Encoding
字段,就表示客户端不支持压缩数据;如果响应报文里没有 Content-Encoding
字段,就表示响应数据没有被压缩
语言类型使用的头字段
同样的,HTTP 协议也使用 Accept
请求头字段和 Content
实体头字段,用于客户端和服务器就语言与编码进行"内容协商"。
Accept-Language
字段标记了客户端可理解的自然语言,也允许用","做分隔符列出多个类型,例如:
makefile
Accept-Language: zh-CN, zh, en
这个请求头会告诉服务器:"最好给我 zh-CN
的汉语文字,如果没有就用其他的汉语方言,如果还没有就给英文"。
相应的,服务器应该在响应报文里用头字段Content-Language
告诉客户端实体数据使用的实际语言类型
css
Content-Language: zh-CN
- 字符集在
HTTP
里使用的请求头字段是Accept-Charset
,但响应头里却没有对应的Content-Charset
,而是在Content-Type
字段的数据类型后面用"charset=xxx
"来表示,这点需要特别注意。 - 例如,浏览器请求
GBK
或UTF-8
的字符集,然后服务器返回的是UTF-8
编码,就是下面这样
makefile
Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8
不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset
,而服务器也不会发送 Content-Language
,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有 Accept-Language
字段,响应头里只会有 Content-Type
字段

内容协商的质量值
在 HTTP 协议里用 Accept
、Accept-Encoding
、Accept-Language
等请求头字段进行内容协商的时候,还可以用一种特殊的"q"参数表示权重来设定优先级,这里的"q"是"quality factor"的意思。
权重的最大值是 1,最小值是 0.01,默认值是 1,如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个";",然后是"q=value"。
这里要提醒的是";"的用法,在大多数编程语言里";"的断句语气要强于",",而在 HTTP 的内容协商里却恰好反了过来,";"的意义是小于","的。
例如下面的 Accept 字段:
bash
Accept: text/html,application/xml;q=0.9,*/*;q=0.8
它表示浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重是0.8。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML
内容协商的结果
内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个Vary字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如:
makefile
Vary: Accept-Encoding,User-Agent,Accept
这个 Vary
字段表示服务器依据了 Accept-Encoding
、User-Agent
和 Accept
这三个头字段,然后决定了发回的响应报文。
Vary
字段可以认为是响应报文的一个特殊的"版本标记"。每当 Accept
等请求头变化时,Vary
也会随着响应报文一起变化。也就是说,同一个 URI
可能会有多个不同的"版本",主要用在传输链路中间的代理服务器实现缓存服务,这个之后讲"HTTP 缓存"时还会再提到

- 数据类型表示实体数据的内容是什么,使用的是
MIME type
,相关的头字段是Accept
和Content-Type
; - 数据编码表示实体数据的压缩方式,相关的头字段是
Accept-Encoding
和Content-Encoding
; - 语言类型表示实体数据的自然语言,相关的头字段是
Accept-Language
和Content-Language
; - 字符集表示实体数据的编码方式,相关的头字段是
Accept-Charset
和 Content-Type; - 客户端需要在请求头里使用
Accept
等头字段与服务器进行"内容协商",要求服务器返回最合适的数据;Accept
等头字段可以用","顺序列出多个可能的选项,还可以用";q=
"参数来精确指定权重