从输入 URL 开始建立前端知识体系
Category(分类): Browser Status: 已更新
本文保留原文从浏览器进程、URL、缓存、DNS、TCP/IP、HTTP、HTTPS、服务器处理、渲染和连接关闭建立知识体系的写法。网络协议和浏览器实现持续演进,文中的“典型流程”应理解为模型:缓存命中、连接复用、Service Worker、HTTP/2、HTTP/3、代理和 BFCache 都可能让真实路径变短或改变。
一、先建立全局地图
从用户输入 URL 到页面可见,可以先记住这条主线:
浏览器进程接收导航
-> 解析 URL、检查缓存和 HSTS
-> DNS 得到地址
-> 复用或建立连接(TCP/TLS 或 QUIC/TLS)
-> 发送 HTTP 请求
-> CDN/代理/Web Server/应用处理
-> 返回响应或重定向
-> 浏览器提交文档给渲染进程
-> DOM/CSSOM/样式/布局/绘制/栅格化/合成
-> 用户交互、资源加载和事件循环继续运行
这条链路涉及多个知识领域:
- 操作系统:进程、线程、虚拟内存、套接字;
- 浏览器:Browser、Network、Renderer、GPU/Viz 和 IPC;
- 网络:DNS、IP、TCP、TLS、HTTP/1.1、HTTP/2、HTTP/3;
- Web 平台:URL、缓存、Service Worker、DOM、CSSOM、事件循环;
- 性能:关键渲染路径、LCP、INP、CLS、缓存和资源优先级。
二、前置内容:浏览器主要进程
以 Chromium 的概念模型为例,浏览器可能包含:
- Browser 进程:窗口、标签页、历史记录、权限和进程协调;
- Renderer 进程:页面 HTML/CSS/JavaScript、DOM、布局和绘制准备;
- Network Service:网络协议和资源请求;
- GPU/Viz 相关进程:图形设备、合成和显示;
- Utility、Storage、音视频、扩展等进程:按功能动态创建。

早期资料常写“每个 Tab 对应一个渲染进程”。这只是便于理解的粗略模型。现代浏览器会根据站点隔离、同源关系、页面策略、扩展和资源压力决定进程归属,一个页面也可能与多个进程协作。Firefox、Safari 和 Chromium 的划分也不完全相同。
多进程的优点是隔离崩溃和权限边界、利用多核并行工作、便于回收页面资源;代价是进程、线程、IPC、共享内存和图形表面会增加资源消耗。
三、第一部分:输入网址并解析 URL
如果地址栏输入的内容符合 URL 形式,浏览器会解析它;如果只是普通文本,浏览器可能交给默认搜索引擎。URL 常见结构如下:

https://example.com:443/docs/index.html?lang=zh-CN#install
\___/ \_________/ \___/ \_____________/ \________/ \______/
协议 主机 端口 路径 查询参数 片段
用户名、密码、端口、查询参数和片段并不是每个 URL 都有。片段通常只在客户端定位文档内位置,不会发送给服务器。URL 中的国际化域名会经过规范化和编码,路径、查询参数的编码也会影响最终请求目标。
1. 解析前的浏览器行为
浏览器可能先处理:
- 历史记录、书签和地址栏自动补全;
- 当前页面的
beforeunload(如果页面注册了且浏览器允许),但不应依赖它保存可靠数据; - BFCache、预渲染和页面恢复;
- 浏览器缓存、Service Worker 和预加载资源。
beforeunload 可能影响 BFCache,也不是移动端页面关闭的可靠通知。需要保存数据时应在状态变化时保存,页面隐藏时可做最后一次轻量上报。
2. HSTS:把 HTTP 升级为 HTTPS
HSTS 由服务端响应头声明:
Strict-Transport-Security: max-age=31536000; includeSubDomains
浏览器记住后,用户输入 http://example.com 时可以在发出明文 HTTP 请求前改成 https://example.com。HSTS 仍需 DNS 和 HTTPS 连接,且不能替代证书验证。首次访问前是否受保护,取决于预加载列表或其他已有策略。
四、浏览器缓存
缓存不是只有一个“浏览器缓存开关”,而是由 HTTP 缓存、内存/磁盘存储、Service Worker、CDN 和代理等共同影响。具体读取顺序由请求上下文和浏览器实现决定。



1. 强缓存
Cache-Control: public, max-age=3600
Expires: Wed, 22 Nov 2026 08:41:00 GMT
Cache-Control: max-age=3600表示响应在一小时内通常可以直接复用;no-cache表示复用前需要重新验证,不是禁止存储;no-store表示不要存储响应;private表示响应不应被共享缓存用于其他用户;immutable可用于版本固定、内容指纹化的静态资源,但应谨慎使用;Expires是绝对时间,现代项目应优先使用Cache-Control。
请求头和响应头可以携带不同方向的缓存指令。不要把所有字段放进“请求头表”或“响应头表”后就认为它们含义相同。
2. 协商缓存
强缓存不能直接使用,或响应要求重新验证时,浏览器会携带缓存验证器:
GET /app.js HTTP/1.1
If-None-Match: "app-v42"
If-Modified-Since: Wed, 22 Nov 2023 08:41:00 GMT
资源没有变化时,服务器可以返回:
HTTP/1.1 304 Not Modified
ETag: "app-v42"
304 没有新的响应体,浏览器继续使用已有缓存。资源变化时,服务器返回新的 200 响应和新的验证器。
3. 用 Last-Modified 实现一个安全的 Node.js 示例
下面的示例只用于说明 HTTP 验证器。生产环境还应处理路径穿越、并发、文件不存在、时区格式、缓存控制和错误响应:
import http from 'node:http'
import fs from 'node:fs'
import path from 'node:path'
const filePath = path.resolve('public/index.js')
http.createServer((request, response) => {
if (request.url !== '/index.js') {
response.writeHead(404)
response.end('Not Found')
return
}
const stat = fs.statSync(filePath)
const lastModified = stat.mtime.toUTCString()
const requestTime = request.headers['if-modified-since']
if (requestTime === lastModified) {
response.writeHead(304, { 'Last-Modified': lastModified })
response.end()
return
}
response.writeHead(200, {
'Content-Type': 'text/javascript; charset=utf-8',
'Cache-Control': 'no-cache',
'Last-Modified': lastModified
})
fs.createReadStream(filePath).pipe(response)
}).listen(8888)



Last-Modified 的精度通常是秒,快速连续修改或分布式文件系统可能让它不够精确,所以常与 ETag 一起使用。
4. 用 ETag 实现验证
import crypto from 'node:crypto'
function createEtag(buffer) {
const digest = crypto.createHash('sha256').update(buffer).digest('base64url')
return `"${digest}"`
}
const body = fs.readFileSync(filePath)
const etag = createEtag(body)
if (request.headers['if-none-match'] === etag) {
response.writeHead(304, { ETag: etag })
response.end()
} else {
response.writeHead(200, {
'Content-Type': 'text/javascript; charset=utf-8',
'Cache-Control': 'no-cache',
ETag: etag
})
response.end(body)
}



ETag 不要求必须是文件 MD5,也可以是版本号、哈希或其他稳定标识。多台服务器应尽量生成一致的 ETag,否则同一资源在不同节点间可能反复验证。
5. Service Worker、内存缓存和磁盘缓存
Service Worker 运行在独立的 Worker 全局环境中,可以拦截符合范围的 fetch 事件,并用 Cache Storage 实现离线或自定义缓存策略。它需要安全上下文(HTTPS,开发环境的 localhost 通常例外):
self.addEventListener('fetch', event => {
if (new URL(event.request.url).pathname === '/app-shell.html') {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request)
})
)
}
})
Memory Cache 读取快但生命周期短,Disk Cache 容量和持续性通常更好。它们是浏览器内部实现,不应依赖某个资源一定在哪一层。Cache Storage、HTTP Cache 和预加载缓存也有不同的匹配规则。
HTTP/2 Server Push 曾经被资料称为 Push Cache,但该机制已经被现代浏览器逐步弃用,Chrome 已移除支持。现在通常使用 preload、103 Early Hints、缓存、CDN 和合理的资源优先级替代,不要把 Push Cache 当作当前浏览器必然存在的缓存层。
五、DNS:域名解析
在连接 Web 服务器前,浏览器需要通过操作系统或配置的 DNS 解析器取得 A/AAAA 等记录。常见缓存位置包括浏览器、操作系统、路由器和递归解析器。

1. 递归查询
通常客户端向递归 DNS 服务器发起一次查询,由递归服务器负责访问根、顶级域和权威服务器,并把最终结果返回给客户端:

2. 迭代查询
迭代查询时,被询问的 DNS 服务器返回下一步的委派信息,查询方继续访问下一台服务器:

现实网络中常见的是“客户端到递归解析器”与“递归解析器向权威服务器迭代”组合。DNS 查询通常使用 UDP,但大响应、截断和安全 DNS 还可能使用 TCP、DoT 或 DoH。
3. DNS 负载均衡和预解析
同一域名返回多个地址,可以结合地域、权重、健康状态和 CDN 节点分散请求:

DNS 负载均衡受到 TTL、缓存、连接复用和健康检查的影响,不能代替所有应用层故障转移。
对确定会使用的跨域资源,可以声明预解析:
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
dns-prefetch 只提示解析域名,preconnect 还可能建立连接和 TLS。二者会消耗连接和设备资源,应只对关键第三方域名使用,不要为所有域名添加。

六、第二部分:网络协议和 TCP 连接
1. 网络协议分层

TCP/IP 不是只有 TCP 和 IP 两个协议,而是一组协议集合:DNS、HTTP、TLS、TCP、UDP、IP、ARP 等在不同层次协作。

2. TCP 三次握手
HTTP/1.1 和 HTTP/2 通常使用 TCP 连接。HTTP/3 则使用 QUIC/UDP,不经过 TCP 三次握手。
典型 TCP 握手:
- 客户端发送
SYN和初始序列号x; - 服务端发送
SYN + ACK,确认x + 1,并发送自己的初始序列号y; - 客户端发送
ACK,确认y + 1。

三次握手让双方确认发送和接收能力,并同步序列号。两次握手不能让服务端确认客户端能收到服务端的确认,延迟到达的旧 SYN 也可能让服务端错误保留连接状态。
TCP 连接还会协商窗口、最大报文段、时间戳、拥塞控制等参数。浏览器和系统可能复用已有连接、使用 TCP Fast Open 或通过代理建立连接,不能把每次请求都当作从零开始。
3. 半连接队列与 SYN 洪泛
服务端收到 SYN 后可能处于 SYN-RECEIVED,连接信息会进入半连接队列;完成握手后才进入已建立连接队列。大量伪造源地址的 SYN 可能耗尽队列,形成 SYN Flood。常见防护包括 SYN cookies、网关清洗、连接限速和系统参数调优。
排查时可以使用系统工具观察连接状态,例如 Linux:
ss -ant state syn-recv
不要仅凭一次状态数量就断定攻击,还要结合流量来源、时间趋势和服务端日志。
七、第三部分:HTTP 请求和版本演进
1. HTTP/0.9、1.0 和 1.1
历史上 HTTP/0.9 只有简单的 GET 和 HTML 响应;HTTP/1.0 引入状态码、请求头、响应头、内容类型和缓存等能力;HTTP/1.1 增加持久连接、分块传输、Host、范围请求和更完整的缓存协商。
HTTP/1.1 的连接可以复用,但同一连接上的请求和响应存在顺序限制,浏览器可能通过多个连接缓解队头阻塞。
2. HTTP/2
HTTP/2 使用二进制帧、流、多路复用、HPACK 头部压缩和优先级等机制。一个连接可以承载多个并发流,流内仍然保持顺序。HTTP/2 Server Push 已被现代浏览器弃用,资源预加载和 CDN 策略更常见。
3. HTTP/3
HTTP/3 使用 QUIC 在 UDP 之上传输,并使用 TLS 1.3。它为不同流提供独立的传输处理,减少 TCP 层队头阻塞影响,但实际性能仍取决于网络、丢包、服务端和客户端:

HTTP/3 不是“无论环境都最快”,也不是把可靠性完全交给 UDP;可靠传输、拥塞控制和加密由 QUIC 实现。
4. HTTP 请求报文
GET /docs/index.html?lang=zh-CN HTTP/1.1
Host: www.example.com
Accept: text/html
Accept-Encoding: gzip, br
Connection: keep-alive

请求通常包含请求行、请求头和可选请求体。Host、Accept、缓存验证器、Cookie、认证和客户端提示等字段会影响服务端处理。
5. HTTP 响应报文
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
Content-Encoding: br
<!doctype html>

HTTP 是无状态协议,但 Cookie、Authorization、URL 参数和服务端会话可以在应用层保存状态。HTTP/1.1 默认通常使用持久连接,HTTP/2 和 HTTP/3 还会复用多路流。
6. 各协议如何配合

可以这样记:
- DNS 将主机名解析为地址;
- HTTP 定义请求和响应的应用语义;
- TLS 提供认证、机密性和完整性保护;
- TCP 或 QUIC 负责可靠传输、拥塞控制和连接;
- IP 负责在网络间寻址和转发。
八、HTTPS 和 TLS
HTTPS 是 HTTP 通过 TLS 传输。现代部署应优先使用 TLS 1.2/1.3,TLS 1.3 的握手和密码套件与旧版 TLS 1.2 不同,不能把一套固定的“客户端四步、服务端三步”流程套给所有版本。

典型 TLS 1.3 握手会包含:
- 客户端发送
ClientHello,携带支持的版本、密码套件、随机数和密钥交换参数; - 服务端选择参数,返回
ServerHello、证书链和签名等信息; - 客户端验证证书的域名、有效期、信任链和用途;
- 双方通过密钥交换导出会话密钥;
- 后续 HTTP 数据使用对称加密并带完整性保护。
TLS 证书主要用于认证服务端身份和签名密钥交换,不是用来直接加密所有业务数据。TLS 1.3 通常需要一次网络往返完成首次握手,会话恢复可能更快;0-RTT 请求有重放风险,服务端要限制适用操作。
九、第四部分:服务器处理请求
服务器链路可能是:
客户端 -> CDN/WAF -> 反向代理 -> Web Server -> 应用服务 -> 缓存/数据库/其他服务

静态资源可以由 CDN 或 Web Server 直接返回;动态请求可能交给 Node.js、Java、Go、PHP、Python 等应用服务,再访问数据库或缓存。服务端可以使用线程池、事件循环、进程模型和异步 I/O 组合处理并发,不应把 epoll 当成所有服务器的唯一实现。
反向代理常用于 TLS 终止、路由、压缩、缓存、限流和负载均衡。部署时要正确传递 Host、协议、真实客户端地址、请求 ID,并处理超时、重试和幂等性。
服务器最终返回状态码、响应头和响应体。重定向时:
301、308适合永久迁移;302、303通常表示临时处理;307、308保留原请求方法和请求体;- 避免重定向链和循环。
十、第五部分:浏览器渲染页面
1. DOM 树
浏览器把 HTML 字节按编码解码、分词并建立节点关系,形成 DOM:

<!doctype html>
<html lang="zh-CN">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<img src="awesome-photo.jpg" alt="示例图片">
</body>
</html>
2. CSSOM 和样式计算
body { font-size: 16px; }
p { font-weight: 700; }
span { color: red; }
img { max-width: 100%; height: auto; }
浏览器会解析 CSS 规则,根据层叠、继承、媒体条件和元素状态计算最终样式,形成供布局使用的样式数据:


3. 布局树、绘制和合成
现代实现可能使用布局树、绘制块、图层树和合成表面,而不一定使用旧 WebKit 资料中的 RenderObject/RenderLayer 名称。概念上可以分为:
- 样式重计算;
- Layout,计算几何位置;
- Paint,生成绘制记录;
- Raster,将绘制记录栅格化为位图;
- Composite,按图层、裁剪、透明度和变换合成。

页面滚动、3D 变换、视频、部分 iframe、滤镜和动画可能促使浏览器使用独立合成资源:
.card {
transition: transform 200ms ease, opacity 200ms ease;
}
.card.is-moving {
transform: translateY(-8px);
opacity: 0.9;
}
不要把 transform: translateZ(0) 当成必须使用的 GPU 开关。will-change 也只是提示:
.card.is-preparing {
will-change: transform, opacity;
}
大量合成层会占用纹理内存、栅格化带宽和合成时间,应通过 DevTools Performance、Layers 和 Paint flashing 验证。


4. 回流与重绘
- 回流/Layout:元素尺寸、位置、内容或布局关系变化后重新计算几何信息;
- 重绘/Paint:颜色、背景、阴影等视觉内容变化后重新生成绘制记录;
- 合成/Composite:组合已经准备好的图层或表面。
“回流必然导致重绘、重绘不一定导致回流”是有用的入门模型,但实际浏览器可能使用缓存和局部更新。下面这些读取可能迫使浏览器提交之前的样式写入并计算布局:
const rect = element.getBoundingClientRect()
const width = element.offsetWidth
const style = getComputedStyle(element)
优化方式:
- 将 DOM 和 class 修改批量执行;
- 将布局读取与样式写入分组;
- 缓存需要重复读取的值;
- 使用
DocumentFragment或一次性更新; - 动画优先考虑
transform和opacity; - 对大规模计算使用 Worker 或分片任务;
- 不要把现代 CSS
calc()误认为必须禁用的“CSS 表达式”。
十一、第六部分:连接关闭和 TCP 四次挥手
HTTP/1.1 的持久连接、HTTP/2 的多路复用和 HTTP/3 的 QUIC 都可能让连接继续存在。连接何时关闭由协议、服务器、客户端、空闲超时和网络状态决定。
TCP 主动关闭时通常包括:
- 一方发送
FIN,表示不再发送数据; - 对方返回
ACK,但仍可以继续发送自己的数据; - 对方发送自己的
FIN; - 第一方确认并进入
TIME_WAIT等状态。

实际 ACK 和 FIN 可能合并,因此“四次挥手”是典型交互模型,不是每次都严格四个独立报文。TIME_WAIT 用来处理延迟报文和最后确认重传,具体时长由 TCP 实现和网络参数决定。
十二、如何用工具验证这条链路?
浏览器 DevTools
- Network:观察 DNS、连接复用、TLS、请求、响应、重定向、缓存和资源优先级;
- Performance:观察脚本、样式、布局、绘制、栅格化、合成和长任务;
- Application:检查 HTTP 缓存、Service Worker、Cache Storage 和存储;
- Rendering/Layers:检查绘制闪烁和合成图层;
- Search Console:检查搜索引擎实际抓取和渲染结果。
Performance API
const navigation = performance.getEntriesByType('navigation')[0]
if (navigation) {
console.table({
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
ttfb: navigation.responseStart - navigation.startTime,
domContentLoaded: navigation.domContentLoadedEventEnd,
load: navigation.loadEventEnd
})
}
不要把一次开发机结果当成所有用户体验。真实监控应按页面、发布版本、设备、网络、地域和导航类型统计 P75/P95,并结合 LCP、INP、CLS、资源时序和错误数据分析。



十三、把“固定数字”改成“验证口径”
原文中有一些典型但过时的固定说法,建议这样理解:
| 旧说法 | 更准确的现在说法 |
|---|---|
| 每个 Tab 必然一个进程 | 进程归属由浏览器根据站点隔离和资源策略决定 |
| DNS 一定使用 UDP | 常用查询可能使用 UDP,也可能使用 TCP、DoT、DoH |
| HTTP/1.1 同域名最多 6 个请求 | 这是旧浏览器经验;HTTP/2/3 使用多路复用,实际由协议和实现决定 |
| HTTPS 固定 3 次 TCP + 4 次 TLS 握手 | TLS 版本、恢复、代理和 HTTP/3 会改变往返次数 |
| HTTP/3 一定最快 | QUIC 有优势,但需结合网络、丢包、服务端和客户端验证 |
| 所有资源都按固定缓存层顺序读取 | Service Worker、HTTP Cache、CDN 和预加载受请求上下文与实现影响 |
translateZ(0) 一定开启 GPU | 现代浏览器会自行决定合成路径,可能增加内存成本 |
页面一定要等 load 才显示 | 浏览器可以流式解析和渐进式渲染,load 不是用户体验完成时间 |
总结
- 浏览器导航是 Browser、Network、Renderer、GPU/Viz 等多个进程和线程协作的结果;
- URL 的片段通常不发送到服务器,缓存和 HSTS 可能让网络步骤提前结束或被跳过;
- DNS 负责解析地址,但递归、迭代、缓存和安全 DNS 的部署方式很多;
- TCP、TLS、HTTP/2、HTTP/3 分别解决不同层的问题,不能把旧 HTTP/1.1 经验套到所有连接;
- 服务器可能经过 CDN、WAF、反向代理、Web Server、应用和数据库;
- 浏览器流式解析 HTML,经过 DOM、CSSOM、样式、布局、绘制、栅格化和合成;
- TCP 四次挥手是典型关闭流程,HTTP 持久连接和 QUIC 会改变连接生命周期;
- 学习浏览器原理的关键不是背诵固定数字,而是掌握每个阶段的职责、依赖和验证工具。