线上排查加载慢时,我一般先从缓存头和连接复用看起,HTTP 的问题大多落在这两处。
Q1: HTTP/1.1 vs HTTP/2 vs HTTP/3 有什么区别?
1
HTTP/0.9 与 HTTP/1.0: 文本协议的诞生
1991 年 HTTP/0.9 只支持 GET 请求和纯文本响应。1996 年 HTTP/1.0 引入了状态码、头部字段和 Content-Type,但每个请求都需要建立新的 TCP 连接。
2
HTTP/1.1 的致命痛点: 队头阻塞
HTTP/1.1 默认开启并规范化了持久连接(Keep-Alive)——该机制在 HTTP/1.0 已有扩展实现,但同一连接中请求必须串行——前一个请求的响应没回来,后一个请求只能等着。浏览器对同一域名限制 6-8 个并发连接,多资源页面加载效率低下。HTTP/1.1 还提出过管线化(pipelining),但响应仍须按请求顺序返回,且主流浏览器默认关闭,实际并未解决队头阻塞。
3
HTTP/2 的革命: 多路复用
HTTP/2 引入了二进制分帧和多路复用——单个 TCP 连接上可以并行传输多个请求/响应。数据被拆分为帧(Frame),不同流的帧交错传输。还引入了 HPACK 头部压缩和服务器推送(Server Push 后来因收益有限被主流浏览器弃用)。
4
HTTP/2 的遗留问题: TCP 层队头阻塞
虽然 HTTP 层的多路复用解决了 HTTP 队头阻塞,但 TCP 是字节流协议——一个 TCP 包丢失,所有流都得等重传。在高丢包率网络下,HTTP/2 的性能可能反而不如 HTTP/1.1 的多连接策略。
5
HTTP/3 的终极方案: QUIC
HTTP/3 用 QUIC(基于 UDP)替代 TCP。QUIC 的流是独立的——Stream 2 的包丢失只阻塞 Stream 2,Stream 1 和 3 正常交付。QUIC 还内置了 TLS 1.3(1-RTT 握手),支持 0-RTT 恢复和连接迁移。
HTTP 协议的演进核心是解决队头阻塞——从 HTTP/1.1 的串行、到 HTTP/2 的多路复用、再到 HTTP/3 的独立流。
HTTP/1.1 的队头阻塞
就像一条单车道公路,前面的车抛锚了,后面的车全部堵死。HTTP/1.1 中一个慢请求会阻塞同连接上的所有后续请求,浏览器只能开 6 条车道(并发连接)来缓解。
HTTP/3 的独立流
HTTP/3 就像一条多车道高速公路,每条车道完全独立。一辆车抛锚只堵自己那条道,其他车道畅通无阻。QUIC 基于 UDP 实现了流级别的独立性,彻底解决了队头阻塞。
Q2: HTTPS 的握手过程是什么?
HTTPS = HTTP + TLS,提供三个保障:机密性(数据加密)、完整性(不可篡改)、身份认证(确认服务器身份)。核心流程如下:证书信任链与 Certificate Transparency
浏览器验证服务器身份依赖证书信任链:根证书(预装在操作系统/浏览器中)签发中间证书,中间证书签发服务器证书。浏览器从服务器证书向上追溯到根证书,逐级验证签名。Certificate Transparency(CT)日志让所有签发的证书都可公开审计,防止 CA 被攻破后签发伪造证书。TLS 1.3 的安全改进
TLS 1.3 移除了所有不安全的加密算法(如 RSA 密钥交换、CBC 模式),只保留 AEAD 加密(如 AES-GCM)。这意味着即使攻击者截获了握手过程中的所有消息,也无法推导出会话密钥——因为密钥交换使用的是 ECDHE(椭圆曲线 Diffie-Hellman 临时密钥),每次连接生成全新的密钥对。Q3: HTTP 缓存策略是什么?
基础梳理
专家视角深度剖析
很多人能背出”强缓存和协商缓存”,但实际更在意的是:在实际工程中,不同资源应该用什么缓存策略?为什么? 缓存策略的核心原则是:不变的资源用强缓存,会变的资源用协商缓存。 具体来说: HTML 文件是入口文件,内容会随版本更新,应该用Cache-Control: no-cache(每次都协商缓存),配合 ETag 让服务器判断是否有变化。JS/CSS/图片等静态资源应该用 Cache-Control: max-age=31536000(强缓存 1 年),配合构建工具生成的 hash 文件名(如 app.a1b2c3.js)——内容变了文件名就变了,自动失效。API 响应通常用 Cache-Control: no-store(完全不缓存)。
Q4: 跨域有哪些解决方案?
CORS 的原理: 浏览器的同源策略限制了跨域请求。CORS(Cross-Origin Resource Sharing)通过在响应头中声明允许的来源,让浏览器放行跨域请求。非简单请求(使用 PUT/DELETE 等方法、自定义请求头,或 Content-Type 不是三个安全值之一)会触发 preflight——浏览器先发 OPTIONS 请求询问服务器是否允许。 同源策略的限制:底层源码:Chromium 是如何实现的?
Chromium 的网络栈是怎么分层的?
Chromium 的网络栈分层实现在 net/ 目录中(详见 Chromium 源码:net/http/http_network_transaction.cc):
SpdySession 管理单个 TCP 连接上的多个流,HTTP/3 通过 QuicSession 管理 QUIC 连接。
QUIC 的连接迁移是怎么实现的?
详见 Chromium 源码:net/quic/quic_session.cc:
及格回答 vs 满分回答
问题:“说说 HTTP/2 和 HTTP/3 的区别”
及格回答(60-70分):“HTTP/2 有多路复用、头部压缩、二进制分帧。HTTP/3 用 QUIC 替代 TCP,解决了 TCP 层的队头阻塞。”问题出在:知道特性但不理解设计权衡。 满分回答(90-100分):
HTTP/2 的核心改进是多路复用——单个 TCP 连接上并行传输多个请求/响应,用流 ID 标识每个请求。配合 HPACK 头部压缩(静态表 + 动态表 + Huffman 编码),重复头部只传索引号。但 TCP 是字节流协议,一个 TCP 包丢失会阻塞所有流——这是 TCP 层的队头阻塞。《High Performance Browser Networking》系统讨论了 HTTP/2 多路复用,并指出 TCP 层的字节流有序交付机制仍是性能瓶颈。[1]
HTTP/3 用 QUIC(基于 UDP)彻底解决这个问题。QUIC 的流是独立的——Stream 2 的包丢失只阻塞 Stream 2,Stream 1 和 3 正常交付。QUIC 还内置了 TLS 1.3(1-RTT 握手),支持 0-RTT 恢复和连接迁移。连接迁移通过 Connection ID 实现——不依赖 IP:Port,Wi-Fi 切换到 4G 时连接不断开。Cloudflare 在《HTTP/3: the past, the present, and the future》中指出,QUIC 由 Google 开发后被 IETF 标准化为开放协议,所有 HTTP/3 连接默认加密(内置 TLS 1.3),这是 HTTP 协议历史上首次在传输层强制加密。[2]
在 Chromium 中,HTTP/2 实现在net/spdy/目录(SPDY 是 HTTP/2 的前身),HTTP/3 实现在net/quic/目录(基于 Google 的 quiche 库)。两者的上层接口统一在net/http/中,对应用层透明。理解 HTTP 协议栈的分层设计(socket → TLS → HTTP),是从”会发请求”到”知道浏览器和服务器之间到底发生了什么”的关键跃迁。
HTTP/2 不一定比 HTTP/1.1 快。在单个请求的简单场景(二进制解析开销)、高丢包率网络(TCP 队头阻塞更严重)、服务器配置不当等情况下,HTTP/2 可能更慢。选择协议要结合实际场景。
常见追问
题目来源于真实面经(字节/腾讯/阿里前端面试、牛客网、haizlin/fe-interview),按难度递进。
HTTP 缓存的优先级是什么?ETag 和 Last-Modified 有什么区别?
HTTP 缓存的优先级是什么?ETag 和 Last-Modified 有什么区别?
答题要点:
不能只答”强缓存优先”,要说清楚
Cache-Control 和 Expires 的优先级差异。高分回答:
“缓存判断的优先级是:强缓存 > 协商缓存。强缓存中,Cache-Control 的优先级高于 Expires——因为 Expires 是绝对时间,受客户端时钟影响(用户改系统时间就会出问题),而 Cache-Control 是相对时间(max-age=3600 表示从响应开始算 3600 秒)。协商缓存中,ETag 的优先级高于 Last-Modified。Last-Modified 精度只到秒——如果文件在 1 秒内修改了多次,或者文件内容没变但修改时间变了(如重新部署),Last-Modified 会误判。ETag 是内容哈希,内容不变就不失效。但 ETag 计算有性能开销(需要读取文件内容生成哈希),所以通常两者配合使用。”CORS 的 preflight 请求是什么?什么时候会触发?
CORS 的 preflight 请求是什么?什么时候会触发?
答题要点:
结合”简单请求”的定义和 preflight 的保护目的解释。高分回答:
“CORS 将请求分为简单请求和非简单请求。简单请求满足三个条件:1)方法是 GET/HEAD/POST;2)头部只有安全字段(Accept、Content-Type 等);3)Content-Type 只限
text/plain、multipart/form-data、application/x-www-form-urlencoded。简单请求直接发送,浏览器检查响应头的 Access-Control-Allow-Origin。非简单请求(如 PUT/DELETE、自定义 Header、application/json 的 Content-Type)会触发 preflight——浏览器先发一个 OPTIONS 请求,询问服务器是否允许该跨域请求。服务器响应允许后,浏览器才发真正的请求。Preflight 的设计初衷是保护旧服务器——旧服务器不理解 CORS,但也不会执行副作用操作(OPTIONS 是安全的)。减少 preflight 的方式:1)用简单请求的 Content-Type;2)服务器配置 Access-Control-Max-Age 缓存 preflight 结果;3)对于频繁的跨域 API 调用,用 Nginx 反向代理消除跨域。”HTTPS 能防止中间人攻击吗?什么情况下 HTTPS 也会被破解?
HTTPS 能防止中间人攻击吗?什么情况下 HTTPS 也会被破解?
答题要点:
必须分场景讨论——HTTPS 的保护范围和边界。高分回答:
“HTTPS 能有效防止中间人攻击,但有几个边界场景需要注意:1)证书伪造:如果攻击者在用户设备上安装了自签名根证书(如企业代理),就能解密所有 HTTPS 流量。这就是为什么企业抓包工具(如 Charles)需要先安装根证书。
2)降级攻击:攻击者拦截 TLS 握手,强制降级到不安全的协议版本。防御方式是服务器配置 HSTS(HTTP Strict Transport Security),强制浏览器只用 HTTPS。
3)证书吊销延迟:CA 吊销证书后,浏览器需要通过 OCSP 或 CRL 查询证书状态。如果浏览器没有及时更新(软失败策略),可能仍然信任已吊销的证书。TLS 1.3 移除了所有不安全的加密算法(如 RSA 密钥交换、CBC 模式),只保留 AEAD 加密(如 AES-GCM),大幅提升了安全性。”
HTTP/2 的服务器推送 (Server Push) 为什么被弃用了?
HTTP/2 的服务器推送 (Server Push) 为什么被弃用了?
答题要点:
对比常见认知,点出 Server Push 的实际问题。高分回答:
“HTTP/2 的 Server Push 理论上很美好——服务器预测客户端需要的资源,在客户端请求前就推送过去,省掉一次往返。但实际使用中发现了严重问题:1)缓存浪费:服务器不知道客户端是否已经缓存了该资源。如果客户端已有缓存,推送就是浪费带宽。HTTP/2 没有好的机制让客户端告知服务器’我已经有了’。
2)优先级冲突:推送的资源和客户端主动请求的资源竞争带宽。如果推送了不重要的资源,可能阻塞了客户端真正需要的关键资源。
3)实现复杂:服务器需要精确预测客户端需要什么资源,这在动态页面中几乎不可能。Chrome 在 v106 中默认禁用了 HTTP/2 Server Push。替代方案是 103 Early Hints——服务器在最终响应前先返回一个 103 状态码,告诉客户端’你可能需要这些资源,先预加载’。客户端可以根据自己的缓存状态决定是否请求,避免了 Server Push 的缓存浪费问题。“
延伸阅读与引用参考
- High Performance Browser Networking:Ilya Grigorik 的免费在线书籍,系统讨论 HTTP/2 多路复用,并指出 TCP 层字节流有序交付仍是性能瓶颈。[1]
- HTTP/3: the past, the present, and the future:Cloudflare 对 HTTP/3 和 QUIC 协议的深度解析,指出所有 HTTP/3 连接默认加密,是 HTTP 历史上首次在传输层强制加密。[2]
- HTTP/2 官方规范 (RFC 7540):HTTP/2 多路复用、流控、HPACK 头部压缩的权威定义。
- QUIC 协议设计文档:Google 对 QUIC 协议的设计哲学和实现细节的深度剖析。
- MDN: HTTP 缓存:Mozilla 对 HTTP 缓存机制的最权威解释,含完整的缓存决策流程图。[3]
