Logo
概览
一次 HTTPS 请求发生了什么?从完整流程到 TLS 握手

一次 HTTPS 请求发生了什么?从完整流程到 TLS 握手

2026年10月13日
2 分钟阅读

为什么这道面试题值得单独梳理

“在浏览器输入一个 HTTPS 地址,直到页面显示出来,发生了什么?”是面试中经常遇到的问题。

难点在于:背得出 DNS、TCP、TLS,却容易在追问时混淆它们各自解决的问题。例如,TCP 已经建立了连接,为什么还要 TLS 握手?既然有了证书,为什么还要交换临时公钥?

这篇文章用一个地址贯穿全程:https://www.example.com/index.html。先建立能复述的主线,再展开 TLS,最后用追问检查理解。

约定场景:需要访问网络、没有可复用连接,使用 HTTP/1.1 或 HTTP/2,并进行基于证书和 ECDHE 的 TLS 1.3 完整握手。 缓存、连接复用和 HTTP/3 会改变这条路径,后文分别说明。

严格地说,“一次 HTTPS 请求”关注请求与响应;“输入地址到页面显示”还包含浏览器渲染。面试时先判断题目问到哪一层。

先记住一条主线:找、连、验、传、画

把一次访问想象成与一家机构办理业务:先找到地址,接通线路,确认对方身份并约定保密方式,再交换材料,最后查看结果。

记忆词对应阶段解决的问题
找URL 解析、缓存检查、DNS要访问什么?能否直接用缓存?对方在哪里?
连TCP 三次握手如何建立可靠、有序的字节流?
验TLS 握手对方是谁?如何获得加密通信所需的密钥?
传HTTP 请求、服务端处理、HTTP 响应要办什么业务?处理结果是什么?
画浏览器解析与渲染如何把收到的内容变成页面?

“验”包含身份认证、密钥协商和握手完整性校验,不能只记成“检查证书”。

URL 与缓存检查
↓ 需要访问网络,且没有可复用连接
DNS:域名 → IP 地址
↓
TCP:建立连接
↓
TLS:认证身份、协商密钥
↓
HTTP:发送请求 → 服务端处理 → 返回响应
↓ 响应是 HTML
浏览器:解析 → 布局 → 绘制 → 合成

再记住三层职责:TCP 管可靠传输,TLS 管通信安全,HTTP 管请求与响应的含义。

对于本文的 TCP 场景,发送数据时可以理解为 HTTP → TLS → TCP → IP,接收时反向处理。DNS 是建立连接前可能需要的查询过程。

第 1~3 步:找到服务器,建立 TCP 连接

1. URL 解析与缓存:先判断是否需要出门

对于 https://www.example.com:443/index.html?name=tom#intro:

部分含义
https使用 HTTPS
www.example.com目标域名
443端口;HTTPS 省略端口时默认使用 443
/index.html请求路径
name=tom查询参数,会随请求发送
intro片段标识,通常用于页面内定位,不随 HTTP 请求发送

浏览器还会根据请求和缓存策略决定是否使用缓存。理解两种情况即可:

  • 缓存可直接复用:例如缓存仍然新鲜,且策略允许使用,可以直接取得内容。
  • 缓存需要验证:例如携带 If-None-Match 向服务器验证 ETag;服务器返回 304 Not Modified 时,浏览器复用已有响应体。这仍然发生了网络请求。

因此,“有缓存”不等于“一定不联网”。缓存复用与验证的规则见 RFC 9111 第 4 节。

如果页面受 Service Worker 控制,请求也可能被它拦截。浏览器还会检查是否有合适的现成连接。这里描述的是逻辑上的判断,实际实现可能并行或提前执行部分工作。

2. DNS:把域名转换成可以连接的地址

DNS 的主要任务是回答:“www.example.com 对应哪些 IP 地址?”

浏览器或操作系统可能已有解析缓存;缓存未命中时,查询配置的递归解析器。解析器也有缓存,必要时才继续向其他 DNS 服务器查询。

浏览器 / 操作系统
↓ 查询
递归解析器
↓ 缓存不足时,按委派关系逐级查询
根 DNS → 顶级域 DNS(如 .com)→ 权威 DNS
↓
解析结果返回客户端

根 DNS 通常提供下一步该问谁的信息,不直接给出网站最终 IP。逐级查询通常由递归解析器完成,也不是每次访问都要从根开始。

DNS 可能返回多个地址;如果使用 CDN,客户端连接的可能是边缘节点。后文的“服务器”首先指当前连接的对端,不一定是最终处理业务的源站。

记忆点:DNS 找地址,不负责证明地址背后的服务器可信。

3. TCP:同步双方的序列号,建立可靠字节流

得到地址后,客户端向目标端口发起 TCP 连接。普通三次握手可以写成:

客户端 服务器
| ---- SYN,seq = x --------------------> |
| <--- SYN + ACK,seq = y,ack = x+1 ---- |
| ---- ACK,ack = y+1 ------------------> |

SYN 用于同步序列号,ACK 表示确认。双方各有自己的初始序列号,第三次握手让服务器确认客户端收到了自己的初始序列号。

为什么不能简单地只握手两次?如果服务器收到一个延迟到达的旧 SYN,仅凭它和自己的回复,不能确认客户端此刻确实要建立这条连接。还需要客户端对本次服务器序列号作出有效确认。防止旧的重复连接请求造成混淆,是三次握手的重要目的。参见 RFC 9293 第 3.5 节。

连接建立后,TCP 通过序列号、确认、重传等机制提供可靠、有序的字节流,但它不验证网站身份,也不加密内容。这正是下一步需要 TLS 的原因。

第 4 步:TLS 如何让这条连接可信、保密

TLS 要提供三项保障:身份认证、机密性、完整性。分别回答“我在和谁通信”“别人能否看懂”“内容被改动后能否发现”。

理解握手前,先解决最常见的混淆:证书里的密钥、交换时用的密钥、传输数据时用的密钥,分别是什么?

三类密钥,各司其职

密钥来源与寿命在本文握手中的用途
证书身份密钥对服务器持有私钥,公钥放在证书里,通常长期使用服务器签名,客户端验签,证明身份
ECDHE 临时密钥对双方为本次握手分别生成交换公钥份额,各自计算共享秘密
对称流量密钥根据共享秘密等材料派生加密并认证握手消息或 HTTP 数据

可以记成:身份密钥证明“我是谁”,临时密钥帮助“算秘密”,流量密钥负责“护数据”。

公钥可以公开,私钥留在本地。最终的数据加密密钥也不会作为明文在网络上传送。

TLS 1.3 的这类握手不会用证书中的 RSA 公钥加密并传送会话密钥。RSA 仍可用于身份签名,要把“RSA 签名”和旧式“RSA 密钥交换”分开理解。

先看握手全貌

以下省略客户端证书认证、会话恢复及握手重试,展示普通 TLS 1.3 完整握手:

客户端 服务器
| ---- ClientHello --------------------> |
| 支持的参数 + 临时公钥份额 A |
| |
| <--- ServerHello --------------------- |
| 选定的参数 + 临时公钥份额 B |
| |
| 双方各自计算共享秘密,派生握手密钥 |
| |
| <--- EncryptedExtensions ------------- |
| <--- Certificate --------------------- |
| <--- CertificateVerify --------------- |
| <--- Finished ------------------------ |
| 以上四项使用握手密钥加密 |
| |
| 验证证书、身份签名和服务器 Finished |
| ---- 客户端 Finished ----------------> |
| ---- 加密的 HTTP 请求 ----------------> |
| <--- 加密的 HTTP 响应 ----------------- |

图中的多条消息不代表多次独立往返,服务器通常可连续发送自己的这一组消息。协议时序可对照 RFC 8446 第 2 节。

Hello:交换能力与临时公钥份额

ClientHello 可以理解为:“这是我支持的配置,以及本次协商用的公开材料。”主要包含:

  • TLS 版本、加密套件、支持的密钥交换组和签名算法等能力。
  • 客户端随机数,以及 key_share 中的临时公钥份额。
  • SNI:告诉服务器要访问哪个域名,便于同一 IP 上的网站选择证书。
  • ALPN:提出支持的应用层协议,例如 h2 和 http/1.1。

服务器通过 ServerHello 选择 TLS 参数,并返回自己的临时公钥份额。在 TLS 1.3 中,ALPN 的选择结果放在后续加密的 EncryptedExtensions 中。

这里还要区分:SNI 是帮助服务器选择网站的提示,证书域名校验才用于检查身份是否匹配。

ECDHE:交换公开材料,各自算出相同秘密

ECDHE 中的 E 表示临时性(Ephemeral)。为了理解其关系,可以用椭圆曲线上的简化记法:

公开约定:曲线及基点 G
客户端:保留私钥 a,发送公钥 A = aG
服务器:保留私钥 b,发送公钥 B = bG
客户端计算:aB = a(bG)
服务器计算:bA = b(aG)
两边得到同一个结果:abG

这里是椭圆曲线上的运算关系,不是普通整数乘法,也不是可以直接照抄的密码实现。实际协议按所选算法从结果中取得共享秘密。

旁观者能看到 G、A、B,却在安全参数下难以计算出私钥或共享秘密。关键在于:双方传送的是公开材料,秘密由双方各自在本地计算出来。

但这一步还没证明身份。中间人可以尝试与两端分别协商两套秘密,因此还需要证书和签名把本次握手绑定到目标服务器。

HKDF:把共享秘密派生成不同用途的密钥

ECDHE 得到的共享秘密不会直接拿来当 AES 密钥。TLS 1.3 使用 HKDF 进行密钥派生,并结合握手消息摘要等上下文。

共享秘密等材料 + 对应阶段的握手摘要
↓ HKDF 分阶段派生
┌─────────┴─────────┐
↓ ↓
握手阶段所用密钥 应用数据所用密钥
↓ ↓
分别区分客户端、服务器的发送方向

所以,“双方协商出一把会话密钥”只是入门简写。准确理解是:双方得到一致的基础秘密,再派生出按用途、按方向区分的密钥。客户端发送用的密钥,对应服务器接收时使用的密钥。

交换完 ServerHello 后,双方已经能派生握手密钥,所以证书等后续握手消息可以加密传送。能够加密通信,此时还不等于已经确认了对方身份。

证书、CertificateVerify、Finished:三项检查不能互相替代

这三项分别回答不同的问题:

检查项核心问题验证依据
Certificate:证书这个公钥能否被信任为目标域名的身份公钥?信任链、域名、有效期、用途等
CertificateVerify:身份证明对方是否持有对应私钥,并认可本次握手内容?用证书公钥验证本次握手的签名
Finished:握手校验密钥材料是否匹配,握手记录是否一致?验证基于派生密钥和握手摘要的 MAC

第一项:证书为什么可信?

典型证书链是:本地信任的根 CA → 中间 CA → 网站证书。客户端验证链上的签名和约束,检查证书有效期、用途,以及 SAN 中的域名是否与目标域名匹配,并按客户端策略处理撤销等检查。

信任的起点来自浏览器或操作系统的信任库。服务器随便发来一张自签名根证书,并不会因此自动获得信任。域名身份匹配规则见 RFC 9525 第 6 节。

第二项:为什么发了证书还要签名?

证书是公开材料,任何人都可能复制。服务器还要用证书对应的私钥,对包含本次握手摘要的规定内容签名,这就是 CertificateVerify。

客户端用证书公钥验签,才能确认对方持有私钥,并将此前交换的关键握手参数绑定到这个身份。攻击者只复制证书或替换临时公钥,都无法凭空生成有效签名。

第三项:Finished 为什么不是一句“结束了”?

Finished 携带基于专用派生密钥和握手摘要计算的消息认证码(MAC)。接收方根据自己的握手记录和密钥重新计算、比较,验证双方密钥材料与握手记录是否匹配;验证失败就终止握手。

它使用共享秘密派生出的密钥进行校验,与 CertificateVerify 使用身份私钥进行数字签名是两种机制。细节见 RFC 8446 的认证消息定义。

客户端完成服务器验证后,发送自己的 Finished,随后即可发送正常应用数据,不需要额外等一句“收到 Finished”。

把 TLS 这一段压缩成一句话:Hello 谈参数,ECDHE 算秘密,HKDF 派密钥,证书与签名验身份,Finished 对记录,AEAD 护数据。 这句概括职责;实际顺序以时序图为准。

第 5~8 步:传送 HTTP 请求、处理业务、展示页面

5. 发送请求:HTTP 表达业务,TLS 保护数据

TLS 准备好后,浏览器可以发送请求。下面用 HTTP/1.1 文本形式帮助理解:

GET /index.html HTTP/1.1
Host: www.example.com
Accept: text/html

方法和路径表达“想做什么”,请求头携带附加信息;请求也可以按需要携带请求体。HTTP/2 保留这些语义,但使用二进制帧编码,并非直接发送上述文本。

对 HTTP 数据的保护过程可以简化成:

HTTP 数据 → TLS 记录层进行 AEAD 加密 → TCP 传输

AEAD 是认证加密,例如 AES-GCM、ChaCha20-Poly1305:既保护内容机密性,也用认证标签检查完整性。接收方解密并校验成功后,才把数据交给上层。

这也是 TLS 不用非对称密码直接加密全部 HTTP 数据的原因:公钥机制适合协商秘密和认证身份,对称认证加密适合高效处理大量数据。

6. 服务端处理:先分清 TLS 在哪里终止

一种可能的部署结构是:

浏览器 → CDN / 反向代理 → 应用服务 → 缓存 / 数据库
↓ ↓
可能直接返回 执行业务并生成响应

这些节点并非必然全部存在。静态资源可能直接由 CDN 返回,也可能根本不需要访问数据库。

如果 TLS 在 CDN 或反向代理处终止,这个节点就能解密浏览器发来的请求。它到源站的连接是另一段链路,是否使用 TLS 取决于部署配置。因此,浏览器到入口的 HTTPS 并不自动说明所有内部链路都已加密。

7. 返回响应:得到状态、元信息和内容

响应包含状态码、响应头,以及适用时的响应体。例如 200 表示请求成功,Content-Type 表示内容类型,响应体可以是 HTML。

响应沿反方向传回,浏览器通过 TLS 解密和校验后交给 HTTP 层处理。若返回重定向,可能继续发起新请求;若是用于缓存验证的 304,则复用已有响应体。

一次请求结束不代表连接结束。 同一条连接可以继续承载后续请求;HTTP/2 还支持在连接内使用多个流并发传送请求和响应。

8. 渲染页面:从内容到像素

当响应是 HTML 时,浏览器的渲染过程可按下面的依赖关系理解:

HTML → DOM ─────┐
├→ 渲染树 → 布局 → 绘制 → 合成
CSS → CSSOM ───┘
  • DOM 表示文档结构,CSSOM 表示样式规则。
  • 渲染树 描述需要呈现的内容及样式,布局计算位置和尺寸。
  • 绘制 产生绘制指令,后续光栅化和合成等工作把内容呈现到屏幕。

实际加载不是等所有内容到齐后才依次开始:浏览器可以边接收边解析,并继续请求 CSS、JavaScript、图片和字体。脚本执行可能阻塞解析或修改 DOM、样式,从而触发后续渲染工作。关于渲染树、布局和绘制,可参阅 web.dev 的渲染流程说明。

如果请求的是 JSON 接口,到响应交给调用方时,请求流程就已完成;它本身不一定触发页面渲染。

面试追问:把容易背错的地方讲明白

TLS 1.3 的 1-RTT,和 TCP 三次握手是什么关系?

RTT 是数据往返一次的时间。“三次握手”数的是三次报文交互,不是三次往返。

在不考虑丢包、握手重试等情况的简化模型中,客户端收到 SYN + ACK 后可发送第三次确认并开始发送 ClientHello。从客户端开始握手到能够继续发起 TLS,约计 1 RTT。

TLS 1.3 把临时公钥份额提前放进 ClientHello,服务器收到后通常就能完成参数选择,返回自己的公钥份额和认证消息。客户端等待这一轮回复、完成验证并发送 Finished 后,即可发送请求,因此完整 TLS 握手通常按 1 RTT 计,不包含前面的 TCP 时间。

阶段简化耗时
DNS取决于缓存与查询路径
TCP 建连约 1 RTT
TLS 1.3 完整握手通常 1 RTT
HTTP 请求到首字节返回约 1 RTT + 服务端处理时间

因此可以估算:

首字节时间 ≈ DNS 时间 + 3 × RTT + 服务端处理时间

这个估算假设使用全新 TCP 连接、普通 TLS 1.3 完整握手,忽略本地计算等额外开销。它是帮助理解延迟来源的模型,不是所有访问的固定耗时,更不是页面完全显示的时间。TLS 如果需要 HelloRetryRequest 补交合适的密钥份额,还会增加往返。

TLS 1.2 和 TLS 1.3 的区别,记哪些就够了?

对比项TLS 1.2TLS 1.3
普通完整握手耗时,不含 TCP通常 2 RTT通常 1 RTT
密钥交换支持 RSA、DHE、ECDHE 等支持 (EC)DHE、PSK 及其组合;移除静态 RSA 密钥交换
对称数据保护支持 AEAD,也包含较旧方案仅使用 AEAD
握手内容保护证书等消息通常明文传送ServerHello 之后的握手消息加密

TLS 1.2 也能使用 ECDHE 并提供前向保密;TLS 1.3 也能使用 RSA 做签名。 不要把协议版本与某一个算法画等号。协议变更见 RFC 8446 第 1.2 节。

证书私钥泄露后,过去的流量还能解密吗?

在正确使用 ECDHE、临时秘密已安全销毁且其他会话秘密未泄露的前提下,仅拿到长期证书私钥,通常无法解密此前录下的流量。这就是前向保密。

原因是:证书私钥负责身份签名,历史会话的数据密钥来自当时的临时密钥交换。身份私钥泄露依然会威胁后续身份认证;前向保密保护的是符合条件的历史会话。

连接复用、会话恢复、0-RTT 是一回事吗?

机制复用了什么对流程的影响
连接复用已经建立好的连接后续请求通常无需重新进行 TCP、TLS 握手
TLS 会话恢复先前握手建立的恢复材料,如关联 Ticket 的 PSK新连接仍要握手,但可以简化认证过程
0-RTT已有 PSK 等条件下的提前发送能力在收到本次服务器握手回复前就发送部分应用数据

会话恢复不等于一定启用 0-RTT,0-RTT 也不代表没有握手或没有网络延迟。

0-RTT 的关键限制是重放风险:攻击者未必能解密请求,却可能把记录的早期数据再次发给服务器。涉及转账、创建订单等不可安全重复的操作,不能直接假定可以放进 0-RTT。相关约束见 RFC 8446 第 8 节。

HTTP/3 为什么不走 TCP 三次握手?

HTTP 版本与 TLS 版本是两个维度。先分清下面三条协议路径:

HTTPS 场景协议关系
HTTP/1.1HTTP/1.1 → TLS → TCP
HTTP/2HTTP/2 → TLS → TCP
HTTP/3HTTP/3 → QUIC → UDP;QUIC 集成 TLS 1.3 握手

HTTP/3 把 HTTP 语义映射到 QUIC,连接建立和安全握手由 QUIC 配合 TLS 1.3 完成,因此没有“先 TCP、再 TLS”这一步骤。UDP 本身不保证可靠传输,所需的可靠性等能力由 QUIC 提供。参见 RFC 9114。

HTTP/2 虽然可以多路复用,但底层 TCP 丢失一段数据时,有序交付会让同一连接上的多个 HTTP 流一起等待。QUIC 使用独立的流交付机制,可以避免这种跨流的 TCP 队头阻塞;丢包仍可能影响对应流,拥塞控制也仍会影响整体传输。

HTTPS 会隐藏所有信息吗?

HTTP 路径、查询参数、请求头和请求体都在 TLS 的保护范围内,但源与目的 IP、流量大小和时间等网络元数据仍可能被观察到。在没有 ECH 等额外保护的普通握手中,SNI 也可能暴露访问域名。

还要记住 TLS 的保护边界:通信端点能够看到解密后的内容,HTTPS 也不代表网站的业务本身一定可信。

一分钟回答:先交代前提,再沿主线展开

下面这段适合在面试中作为第一轮回答,后续再按追问展开 TLS 或浏览器渲染:

我先以没有可用缓存和连接复用、使用 HTTP/2 与 TLS 1.3 的情况说明。浏览器解析 URL,通过 DNS 得到目标地址,再通过 TCP 三次握手建立连接。随后进行 TLS 握手:协商参数,通过 ECDHE 计算共享秘密并派生密钥,验证服务器证书、握手签名和 Finished。浏览器在安全连接上发送 HTTP 请求,服务端处理后返回响应。如果响应是 HTML,浏览器继续加载所需资源,解析 HTML 和 CSS,完成布局、绘制与合成。连接可以继续复用。如果缓存能直接满足请求,或者改用基于 QUIC 的 HTTP/3,流程会有所不同。

回答时抓住三个层次:先说适用场景,再说完整主线,最后点出会改变流程的分支。 不必一开始就罗列全部扩展字段。

合上文章,用六道题检查是否真正记住

先尝试口头回答,再展开核对。

1. TCP 已经建立连接,为什么还需要 TLS?

TCP 提供可靠、有序的字节流;TLS 在其上提供身份认证、机密性和完整性。两者解决的问题不同。

2. ECDHE 把会话密钥发给对方了吗?

没有。双方交换临时公钥份额,保留各自私钥,在本地计算相同的共享秘密,再通过 HKDF 派生流量密钥。

3. 为什么复制一份合法证书,仍然无法完成冒充?

证书是公开材料。攻击者还需要对应私钥,才能对本次握手生成合法的 CertificateVerify 签名;客户端也会检查证书是否匹配目标域名。

4. 证书公钥、ECDHE 公钥、对称流量密钥分别做什么?

证书公钥用于验证身份签名;ECDHE 公钥参与共享秘密计算;对称流量密钥用于加密和认证握手消息或应用数据。

5. 为什么后续请求不一定再经历 DNS、TCP、TLS 全流程?

解析结果可能已缓存,HTTP 内容可能可直接从缓存取得,连接也可能仍然存在并可复用。请求次数、连接次数和完整握手次数不是一一对应的。

6. HTTP/3 用 UDP,可靠性和安全性由谁提供?

QUIC 在 UDP 之上提供流、丢包恢复等传输能力,并集成 TLS 1.3 握手来建立安全连接。它不需要 TCP 三次握手。

复习时先从空白纸写出“找、连、验、传、画”,再在“验”下面补出三类密钥和三项校验。能解释清楚每一步为什么存在,就能沿着这条主线接住大部分追问。