HTTPS(Hypertext Transfer Protocol Secure)是 HTTP 的安全版本,通过在 HTTP 下方加入 SSL/TLS 加密层 ,实现对传输数据的加密、服务器身份认证和数据完整性保护。它默认使用端口 443(HTTP 使用 80)。
HTTP/1.1 、HTTPS 、 HTTP/2 、 HTTP/3 协议结构

SSL/TLS
SSL/TLS 协议并不属于传统的 OSI 或 TCP/IP 模型中的单一层次,而是在传输层(TCP)之上、应用层(HTTP)之下 ,作为一个安全套接层 为上层应用提供加密、认证和完整性保护。其内部又分为两层:记录协议(TLS Record Protocol) 和 握手协议(TLS Handshake Protocol)
TLS 握手是客户端与服务器之间建立安全通信的核心步骤,目的是协商加密算法、交换密钥、验证身份,并生成用于后续加密的会话密钥
TLS 1.2 握手过程(HTTP/2)


TLS 1.3 握手过程 (HTTP/3)
TLS 1.3 相比 TLS 1.2 大幅简化了握手流程,减少了往返次数(RTT),并移除了不安全的加密套件。


第一次握手:客户端 → 服务器(Client Hello)
- 作用:客户端发起连接,发送 Client Hello 消息。
- 内容 :支持的 TLS 1.3 版本、加密套件(AEAD 类)、随机数、密钥共享参数(key_share) ,以及 SNI 扩展(服务器名称指示),指明要访问
api.juejin.cn。
第二次握手:服务器 → 客户端(Server Hello + 加密握手消息)
- Server Hello(明文):服务器选择 TLS 1.3 版本、加密套件,返回自己的 key_share,生成服务器随机数。此时双方已计算出 Pre‑Master Secret 并派生出握手密钥。
- Change Cipher Spec :这是 兼容性消息 (固定值
0x01),在 TLS 1.3 中不是必须的,但某些实现会发送以帮助中间件识别协议切换。 - 第一个 Application Data :实际是加密的 EncryptedExtensions + Certificate + CertificateVerify 消息(因为 TLS 1.3 中除 ClientHello/ServerHello 外所有握手消息都加密)。
- 第二个 Application Data :加密的 Finished 消息(服务器端)。
服务器在这一轮完成了握手消息的发送,并已经可以发送应用数据。
第三次握手:客户端 → 服务器(Client Finished)
- Change Cipher Spec:同样是兼容性消息(可选)。
- Application Data :加密的 Finished 消息(客户端),用于验证握手完整性。
至此,双向握手完成,连接进入 ESTABLISHED 状态,双方可使用 1‑RTT 密钥保护应用数据。
SSL
SSL (Secure Sockets Layer,安全套接层) 是一项历史悠久的网络安全协议。在浏览器地址栏看到的 🔒(安全锁)图标,以及网址开头的 https://,都意味着当前网站正在使用 SSL 技术,确保你的数据在网络上被安全地加密传输。
SSL协议经历了多个版本更新,主要有SSL 1.0、SSL 2.0和SSL 3.0。其中,SSL 2.0版本存在众多安全漏洞,因此逐渐被淘汰。SSL 3.0虽然改善了很多安全问题,但也不再被推荐使用。
TLS 是 SSL 的继任者 ,TLS 1.2 和 TLS 1.3 是目前广泛使用的主流安全协议。
HTTPS 项目 生成证书
用 openssl 命令快速生成一个自签名的证书
bash
openssl genrsa -out private-key.pem 2048
openssl req -new -key private-key.pem -out csr.pem
openssl x509 -req -days 365 -in csr.pem -signkey private-key.pem -out certificate.pem
HTTP/2 核心特性
HTTP/2(超文本传输协议第二版)是 HTTP 协议的第二个主要版本,基于 Google 的 SPDY 协议开发,于 2015 年正式标准化(RFC 7540)。
RFC 9113: HTTP/2 , 废除了RFC 7540 和 8740。
多路复用
在单一的 TCP 连接上,可以并行、交错地发送多个请求和响应,而无需等待。这显著提升了页面加载速度,可比 HTTP/1.1 快 15%-50% ,并降低了延迟。

为什么 HTTP/1.1 很慢?
HTTP/1.1 在传输数据时存在一个先天的性能瓶颈:队头阻塞。
- 串行处理 :在一个 TCP 连接上,请求必须严格按照
"请求1 -> 响应1 -> 请求2 -> 响应2"的顺序进行,前一个请求未完成,后面的所有请求都会被阻塞排队。 - 并发补偿 :为了缓解这个问题,浏览器会 为每个域名开启多个(通常是
6-8个)TCP 连接 来并行加载资源。但这种方式会消耗更多服务器资源,增加连接管理的开销,在高延迟网络下效果依然有限。
如何实现多路复用?
HTTP/2 的解决方案是在 HTTP 层和 TCP 层之间,增加了一个全新的 二进制分帧层,将通信单位细化到无法再分的小"帧"。
多路复用是 HTTP/2 协议 的特性,它在 应用层 实现,具体由浏览器(客户端)和 Web 服务器完成。
浏览器端(发送与接收)
- 发送时 :浏览器将多个请求拆分成带
Stream ID的二进制 帧,交错发送到同一个 TCP 连接上。 - 接收时 :浏览器根据帧头的
Stream ID将收到的帧重组回原始的请求/响应数据。
这些操作完全在浏览器的 HTTP/2 协议栈 中完成,不依赖底层网络。
二进制分帧
HTTP/2 不再使用 HTTP/1.1 的纯文本格式,而是将所有消息分割为更小的二进制"帧"进行传输和解析,效率更高。
帧格式
http
HTTP Frame {
Length (24),
Type (8),
Flags (8),
Reserved (1),
Stream Identifier (31),
Frame Payload (..),
}
- 长度 (24 bits):帧负载的长度(不包括头部),最大 16,384 字节 (2^14)。
- 类型 (8 bits):帧的类型,决定负载的格式和语义。
- 标志 (8 bits):特定于帧类型的布尔标志。
- 流标识符 (31 bits):唯一标识一个流,用于将帧归属到特定的请求/响应。最高位保留。
- 帧负载:可变长度,依据帧类型不同而不同。

头部压缩
HTTP/2 引入了 HPACK 压缩机制,专门用于压缩 HTTP 请求和响应的头部字段,以解决 HTTP/1.1 中头部重复、冗余导致的大开销问题。
为什么需要头部压缩?
- HTTP/1.1 头部是纯文本,每个请求都携带大量重复字段(如
Cookie、User-Agent、Accept等)。 - 头部大小可达数百字节甚至上千字节,频繁传输会消耗带宽、增加延迟。
- 尤其对于小资源(如 API 响应),头部可能比 body 还大。
HPACK 的核心原理?
1、静态字典(Static Table)
- 预定义了一个包含 61 个常见头部字段的静态表(如
:method: GET、:status: 200、content-type等)。 - 每个条目都有一个索引号。发送时可以用索引代替完整字符串,例如用
2表示:method: GET。
2、动态字典(Dynamic Table)
- 在连接过程中动态构建,用于存储之前出现过的自定义头部(如
X-Custom-Header)。 - 双方共同维护,并可通过
SETTINGS帧调整大小。 - 后续请求中,相同头部可以用索引引用,无需重复发送。
3、哈夫曼编码(Huffman Coding)
对头部值(如 text/html,application/json)进行哈夫曼编码,进一步减少字节数。
压缩过程?
发送端:
- 将头部字段拆分为键和值。
- 先在静态字典中查找索引;找不到则在动态字典中查找。
- 如果找到,发送索引。
- 如果未找到,使用字面量形式(键和值),并可选地将该条目加入动态字典。
- 对字符串值进行哈夫曼编码(可选)。
接收端:
- 解码时还原为原始头部字段,并更新动态字典。
服务器推送 (Server Push)
允许服务器在客户端明确请求前,主动推送其可能需要的资源(如 CSS、JS),减少请求次数
问题:推送未请求的资源浪费带宽,缓存命中率难控制,现在更推荐使用 103 Early Hints 或 preload。
HTTP/2 推送的替代方案:103 Early Hints、preload
103 Early Hints
HTTP/1.1 和 HTTP/2 中引入的信息性状态码(由 RFC 8297 定义)。
HTTP 103 Early Hints 是一个信息型 HTTP 状态码,允许服务器在最终响应前,先发送一个包含"提示"的中间响应,让浏览器能提前进行资源加载或连接准备,从而利用服务器的"思考时间"优化性能
在 Chrome 开发者工具的 Network 面板中,103 状态码本身通常不会单独显示为一行 ,而是将 103 中包含的 Link 头部归类到"Early hints headers"这个折叠区域。

"Early hints headers" 下出现了 Link: </style.css>; rel=preload; as=style,这表示浏览器已经收到了 103 响应,并从中提取出了预加载提示。
响应头 link
HTTP 响应头中的 Link 字段用于表达当前资源与其他资源之间的关联关系 ,类似于 HTML <link> 标签的功能,但位于 HTTP 协议层面。它允许服务器在响应中携带链接信息,让客户端(如浏览器)在不解析 HTML 的情况下提前获知并采取相应动作,从而优化性能。
-
资源预加载 (preload) 。
Link: </css/main.css>; rel=preload; as=style。告知浏览器提前加载后续需要的资源(如样式表、脚本、字体等),减少页面渲染等待时间。 -
预连接 (preconnect) 。
Link: <https://example.com>; rel=preconnect, 提前与第三方源建立连接(DNS、TCP、TLS),加速后续请求。 -
预取 (prefetch) 。
Link: </next-page.html>; rel=prefetch, 在空闲时预取下次导航可能用到的资源。 -
DNS 预解析 (dns-prefetch) 。
Link: <https://example.com>; rel=dns-prefetch, 提前解析域名。
示例 返回响应头 link
项目地址 https://localhost:5177/react19-vite-app/bug-view
发起请求 https://localhost:8843/api/html
响应头有返回 link </style.css>; rel=preload; as=style

针对 link ,会根据 link 信息发起请求

node 相关接口代码

为什么 HTTP/2 的服务器推送(Server Push)逐渐被 103 + preload 取代?
HTTP/2 Server Push 的问题:
- 推送的资源可能已被浏览器缓存(浪费带宽)。
- 优先级难以控制,可能导致关键资源被非关键资源抢占。
- 连接复用容易出错(例如推送了但不使用的资源)。
- Chrome 等浏览器已弃用并移除对 HTTP/2 Server Push 的支持(2022 年起)。
流优先级
HTTP/2 允许客户端为每个流(即每个请求/响应)指定 优先级,以告诉服务器哪些资源更重要、应该更早或分配更多带宽。这有助于优化页面加载性能,尤其是首屏关键资源。
HTTP/2 使用 依赖树(Dependency Tree) 和 权重(Weight) 来表达优先级。
流依赖(Stream Dependency)
- 一个流可以依赖于另一个流,表示它应该在依赖的流之后处理(更次要)。
- 例如:
script.js依赖于style.css,则表示服务器应优先发送style.css。 - 所有流最终都隐式依赖于 根流(ID 为 0x0)。
权重(Weight)
- 每个流可以分配一个 1~256 的整数权重,默认 16。
- 同级的流按权重比例分配带宽(资源)。
- 例如:同级两个流,权重 200 和 100,则分别获得约 2/3 和 1/3 的带宽。
排他性标志(Exclusive Flag)
- 将一个流设置为排他依赖时,该流的父流原有的所有子流会变为当前流的子流。
- 简化表达:
Stream A排他依赖Stream P→P的其他旧子流成为A的子流。
优先级信息的传递
- 优先级信息通过 HEADERS 帧 或 PRIORITY 帧 发送。
- HEADERS 帧(创建流时)可以携带优先级字段(依赖流 ID + 权重 + 排他标志)。
- PRIORITY 帧(独立发送)可用于随时调整已存在流的优先级。
HTTP/2 请求头
HTTP/1.1 的 请求行 (GET /index.html HTTP/1.1)在 HTTP/2 中被拆分为多个以 : 开头的伪头字段。
HTTP/2 伪头字段必须在普通头字段之前发送,且不能重名。
HTTP/2 单个HEADERS 帧载荷默认最大 16KB ,但可以通过 SETTINGS_MAX_FRAME_SIZE 调整到 16MB。如果头部超过单帧大小,可以连续发送多个 CONTINUATION 帧进行拼接(但性能可能受影响)。

HTTP/2 协议协商机制:ALPN 与 Upgrade 头
HTTP/2 的协商方式取决于连接是否使用 TLS(即 HTTPS 或 HTTP)。主流方式是 ALPN (Application-Layer Protocol Negotiation),主要用于加密连接;而明文的 HTTP/2(h2c)则需要使用 Upgrade 头。
ALPN 是 TLS 扩展,允许客户端和服务器在 TLS 握手期间协商出应用层协议(如 h2、http/1.1)。
ALPN如何工作?
ALPN的协商过程非常高效,它巧妙地融合在了TLS握手这个必经之路上,没有额外的网络开销。
- 客户端(如Chrome)发送
ClientHello:在TLS握手的第一步中,客户端就通过一个扩展,主动列出自己支持的应用层协议列表,并按优先级排序(例如,首选h2,其次为http/1.1)。 - 服务器(如Nginx)回复
ServerHello:服务器收到列表后,会从其中选择一个自己同样支持的协议。这个选择结果会通过ServerHello消息,以明文形式清晰地告知客户端。 - 达成一致:至此,通信双方就明确了后续将使用何种协议(如HTTP/2),整个过程在TLS的第一次往返(1-RTT)中就完成了。
浏览器如何自动设置优先级(Chrome 的资源优先级)
Chrome 浏览器有一套自动、复杂的优先级调度机制。它的核心逻辑是:让最关键的资源插队,让次要资源待命,从而优先保障用户的第一印象。
不过,还可以手动控制:
- fetch 请求,options 配置 priority 属性
- 调整元素属性 : 对于图片(
<img>)、脚本(<script>)等元素,直接添加fetchpriority属性 - 使用资源提示 (
<link>) : 通过<link>标签发出资源提示,可以对优先级进行间接控制。rel="preload":用于提高 当前页面关键资源 的优先级,指示浏览器尽早加载,通常在<head>标签内声明。rel="prefetch":用于降低 当前页面次要资源的优先级,指示浏览器在空闲时预取。rel="preconnect":不与具体资源挂钩,而是提醒浏览器尽早建立连接。