技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTBR

从输入 URL 到页面展示,到底发生了什么?

所属馆藏
Browser
文件格式
Markdown
原始路径
Browser/12-从输入 URL 到页面展示到底发生了什么?
本文目录14 个章节
  1. 一、总览:一次导航可能经历什么?
  2. 二、输入地址:URL 的组成和导航意图
  3. 三、缓存和 HSTS 可能改变导航路径
  4. 四、DNS:把主机名解析成地址
  5. 五、建立连接:TCP、QUIC 和 TLS
  6. 六、发送 HTTP 请求
  7. 七、服务器处理请求
  8. 八、服务器返回 HTTP 响应
  9. 九、浏览器准备文档和渲染进程
  10. 十、HTML 中的子资源请求
  11. 十一、连接关闭和页面后续活动
  12. 十二、把链路用于性能分析
  13. 总结
  14. 参考资料

从输入 URL 到页面展示,到底发生了什么?

Category(分类): Browser Status: 已更新

本文保留原文从 URL、DNS、TCP、HTTP、服务器处理到浏览器渲染的完整链路,并把 HTTP/2、HTTP/3、TLS 1.3、缓存、Service Worker、现代浏览器进程和渲染流程补充进来。实际网络可能复用连接、命中缓存、经过代理或使用 QUIC,因此下面是一条便于理解的典型路径,不是每次导航都会完整执行的固定步骤。

一、总览:一次导航可能经历什么?

输入并解析 URL
  -> 浏览器判断导航策略、历史记录和缓存
  -> HSTS/HTTPS 升级(如适用)
  -> DNS 解析(如没有可用缓存)
  -> 复用或建立连接:TCP + TLS,或 QUIC + TLS
  -> 发送 HTTP 请求
  -> 处理重定向、缓存验证和服务器响应
  -> 浏览器准备或复用渲染进程
  -> 解析 HTML、加载子资源、计算样式、布局、绘制、栅格化、合成
  -> 页面交互和后续资源继续加载

页面可能在 HTML 尚未完整下载时就开始解析和绘制;如果命中内存缓存、Service Worker、BFCache 或预渲染,部分网络步骤甚至不会发生。

二、输入地址:URL 的组成和导航意图

URL(Uniform Resource Locator)常见组成如下:

https://user:password@www.example.com:443/path/page.html?query=value#section
\___/   \________________/ \_____________/ \___/ \__________/ \____/
协议          用户信息            主机和端口   路径      查询参数     片段

并不是每个 URL 都包含用户名、密码、显式端口、查询参数或片段。片段(#section)通常只由浏览器在客户端处理,不会作为 HTTP 请求的一部分发送给服务器。输入的字符串如果不像 URL,浏览器可能交给默认搜索引擎处理。

浏览器还会结合历史记录、书签、预加载、自动补全和已有页面状态展示提示。地址栏出现预览或搜索建议不等于目标页面已经完成网络导航。

三、缓存和 HSTS 可能改变导航路径

浏览器可能先检查导航缓存、Service Worker 和 HTTP 缓存,再决定是否需要网络请求。这个过程受请求方法、缓存模式、响应头、Vary、凭证、存储分区和浏览器实现影响,不能用一个固定的“先强缓存、再协商缓存、最后网络”列表解释所有场景。

1. 强缓存和协商缓存

服务端可以使用 Cache-ControlExpiresETagLast-Modified 等字段控制缓存:

Cache-Control: public, max-age=31536000, immutable
ETag: "assets-v42"
Last-Modified: Wed, 22 Nov 2023 08:41:00 GMT
  • max-age 表示响应在指定时间内通常可以直接使用;
  • no-cache 不是“不允许缓存”,而是使用前需要向服务器重新验证;
  • no-store 才是要求不要存储响应;
  • private 适合不应被共享缓存复用的用户相关响应;
  • ETagLast-Modified 用于缓存验证,验证未变化时服务器通常返回 304 Not Modified
  • Expires 是 HTTP/1.0 时代的绝对时间,仍可作为兼容字段,但容易受客户端和服务器时钟差异影响。

示例:

GET /app.js HTTP/1.1
If-None-Match: "assets-v42"
If-Modified-Since: Wed, 22 Nov 2023 08:41:00 GMT

如果服务器确认资源未改变:

HTTP/1.1 304 Not Modified
ETag: "assets-v42"

实际服务器应根据标准处理验证器优先级,不能简单把 ETag 当成“文件 MD5 必须相同”。弱 ETag、压缩变体和分布式部署都需要统一策略。

2. HSTS

HSTS(HTTP Strict Transport Security)通过响应头告诉浏览器:在指定时间内把该主机的 HTTP 导航升级为 HTTPS:

Strict-Transport-Security: max-age=31536000; includeSubDomains

浏览器如果已经记住 HSTS,或站点在预加载列表中,用户输入 http:// 时可能在发出 HTTP 请求前就改为 HTTPS。升级并不意味着可以跳过 DNS;浏览器仍需要找到 HTTPS 主机的地址。HSTS 也不是证书校验的替代品,首次访问前的保护能力取决于预加载、已有策略和部署方式。

四、DNS:把主机名解析成地址

浏览器、操作系统、局域网设备、递归 DNS 解析器和权威 DNS 服务器都可能缓存记录。缓存命中时可以直接得到 A(IPv4)或 AAAA(IPv6)记录;没有命中时,递归解析器可能向根、顶级域和权威服务器查询。

DNS 递归解析流程示意

1. 递归与迭代

通常,浏览器或操作系统中的 stub resolver 把问题交给一个递归解析器,并等待它返回最终结果。递归解析器在后台可能采用迭代方式依次询问根服务器、.com 等顶级域服务器和目标域名的权威服务器:

DNS 递归查询示意

在迭代查询中,被询问的服务器返回“下一步应询问谁”的引用,查询方自己继续询问:

DNS 迭代查询示意

实际网络中,客户端直接迭代访问根和权威服务器并不常见;“本地 DNS 负责递归、权威服务器提供委派”更接近常见部署。

2. DNS 名称空间和记录

DNS 是分层的名称空间:根域位于最上层,下面是顶级域和权威管理的域。常见记录包括:

  • A:IPv4 地址;
  • AAAA:IPv6 地址;
  • CNAME:别名;
  • NS:权威名称服务器;
  • MX:邮件服务器;
  • TXT:文本和验证信息;
  • HTTPS/SVCB:服务连接参数(浏览器支持情况取决于版本)。

DNS 名称空间示意

DNS 结果包含 TTL,递归解析器会据此决定缓存时间。TTL 到期不代表记录必然立刻失效于所有地方,也不保证修改能在全球同时生效。

3. DNS 不只有 UDP

普通 DNS 查询经常使用 UDP,但大响应、截断、区域传送等场景可能使用 TCP。DoT 通过 TLS 传输 DNS,DoH 通过 HTTPS 传输 DNS;浏览器还可能使用自己的安全 DNS 或操作系统配置。不要把“浏览器向 DNS 服务器发送 UDP 包”当成所有现代连接的必经步骤。

4. DNS 负载均衡和 CDN

同一域名可以返回多个地址,系统也可能根据地域、网络、健康状态和权重把用户引导到不同节点:

DNS 负载均衡示意

DNS 负载均衡只是流量调度的一部分。它受到 TTL、缓存、连接复用和健康检查延迟影响,不能替代应用层负载均衡和故障转移。

五、建立连接:TCP、QUIC 和 TLS

解析得到地址后,浏览器根据 URL 的端口和协议选择连接方式:

  • HTTP/1.1、HTTP/2 通常运行在 TCP 上;
  • HTTPS 在 TCP 之上增加 TLS;
  • HTTP/3 运行在 QUIC 之上,QUIC 使用 UDP,并在协议中集成 TLS 1.3 握手和传输控制;
  • 如果已经存在可复用的连接,浏览器可能不需要重新握手。

源端口由操作系统从可用的临时端口范围中选择,不能简单写成固定的 1024 < port < 65535

1. TCP 三次握手

典型 TCP 建连过程:

  1. 客户端发送 SYN,携带自己的初始序列号;
  2. 服务端返回 SYN + ACK,确认客户端序列号,并携带自己的初始序列号;
  3. 客户端发送 ACK,确认服务端序列号,连接进入可传输状态。

TCP 连接示意

三次握手的意义是让双方确认对方的发送和接收能力,并同步序列号,避免旧连接请求造成错误状态。实际协议还包含重传、窗口、拥塞控制、SYN cookies 和连接队列等机制。

2. TLS 握手

HTTPS 不只是“HTTP 加一个加密算法”。TLS 握手通常包含:

  • 协商协议版本和密码套件;
  • 服务端发送证书链,客户端验证域名、有效期、签发链和用途;
  • 通过密钥交换建立会话密钥;
  • 后续 HTTP 数据使用对称加密和完整性保护传输。

TLS 1.3 通常比早期 TLS 版本减少握手往返次数,恢复会话时还可能使用 0-RTT,但 0-RTT 数据存在重放风险,服务端不能把它当成普通请求无条件处理。TLS 握手消息数量会随版本、恢复、客户端认证和扩展变化,不能固定说“HTTPS 必须 3 次 TCP + 4 次 TLS 握手”。

HTTP/3 的 QUIC 把传输和 TLS 1.3 结合起来,不经过 TCP 三次握手;它不是“所有场景最快”,实际速度还取决于网络、丢包、服务端和浏览器支持。

六、发送 HTTP 请求

连接建立或复用后,浏览器发送请求行、请求头和可选请求体:

GET /browser/guide?lang=zh-CN HTTP/1.1
Host: www.example.com
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, br
Accept-Language: zh-CN,zh;q=0.9
User-Agent: example-browser

GET 通常没有业务请求体,POSTPUT 等可以携带请求体。浏览器可以发起多种 HTTP 方法,是否允许某种方法还取决于 CORS、服务端和请求上下文,不是“浏览器只能发 GET 或 POST”。

HTTP/1.1 可以使用持久连接,但同一域名最多 6 个连接不是通用标准,浏览器和服务器会根据协议、网络和实现调整。HTTP/2 在一个连接上使用多路复用的流,HTTP/3 使用 QUIC 流,不能把 HTTP/1.1 的连接数经验套用到它们。

七、服务器处理请求

请求可能先到 CDN、WAF、反向代理或负载均衡器,再到 Web Server 和应用服务:

反向代理和应用服务器示意

典型处理链可能包括:

  1. 接收连接并解析 HTTP;
  2. 检查安全策略、认证、限流和缓存;
  3. 由 Nginx、Apache、网关或 CDN 处理静态资源;
  4. 把动态请求转发给应用服务;
  5. 应用查询数据库、缓存或其他服务;
  6. 生成响应并设置状态码、缓存、压缩和安全响应头。

服务端可以使用线程池、事件循环、异步 I/O、进程模型或它们的组合。epoll、线程数和连接数是实现细节,不应把某一种模型称为所有 Web Server 的唯一工作方式。

反向代理能够隐藏应用服务器、终止 TLS、做缓存和负载均衡,但还需要正确处理 HostX-Forwarded-*、超时、重试、幂等性和真实客户端地址。

八、服务器返回 HTTP 响应

响应由状态行、响应头和可选响应体组成:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Encoding: br
Cache-Control: no-cache
Content-Length: 1234

<!doctype html>
<html lang="zh-CN">...</html>

状态码类别:

  • 1xx:信息响应,例如 100 Continue
  • 2xx:成功,例如 200 OK204 No Content206 Partial Content
  • 3xx:重定向或缓存复用,例如 301302303304307308
  • 4xx:请求方错误,例如 400401403404
  • 5xx:服务器处理错误,例如 500502503504

301、302、303、307 和 308

  • 301308 表示永久重定向;
  • 302303 通常用于临时跳转,但历史客户端对 301/302 的方法处理存在兼容行为;
  • 303 明确要求客户端使用 GETHEAD 获取目标;
  • 307308 要求保留原请求方法和请求体。

不要因为 SEO 就说“302 一定比 301 好”。应根据资源是否永久迁移、请求方法和业务语义选择状态码,避免重定向链和循环。

浏览器收到 Location 后可能继续发起下一次导航。最终响应不一定是 200:错误页面、下载响应、204、缓存 304 和跨域响应都会进入不同处理路径。

九、浏览器准备文档和渲染进程

在现代 Chromium 中,网络服务收到响应头后,会把导航状态交给浏览器进程。浏览器根据站点隔离、当前页面和安全策略准备或复用渲染进程,并通过 IPC 提交文档。HTML 响应体随后流入渲染进程,页面可以边接收边解析。

渲染进程通常完成:

  1. 字节按编码解码为字符;
  2. HTML tokenization 并构建 DOM;
  3. 解析 CSS 并构建 CSSOM;
  4. 计算样式并生成布局树;
  5. 处理脚本、事件和资源加载;
  6. 生成绘制记录、分层、栅格化并合成。

1. DOM 树

HTML 解析大致经历“字节 → 字符 → token → 节点 → DOM”的过程:

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <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 和布局树

浏览器根据 CSS 规则、继承和层叠计算节点样式:

body { font-size: 16px; }
p { font-weight: 700; }
span { color: red; }
img { max-width: 100%; }

不可渲染节点不会直接出现在布局树中,但 display: nonevisibility: hidden 的行为不同:前者通常不生成布局盒,后者通常保留布局空间。最终生成的布局树会记录尺寸和位置。

HTML 解析与 DOM 构建示意

3. 样式、布局、绘制和合成

浏览器可能按以下阶段处理页面:

Recalculate Style -> Layout -> Paint -> Raster -> Composite

浏览器渲染阶段示意

阶段之间不是严格的一次性流水线。浏览器会缓存、增量更新、按图块栅格化,也可能让合成线程在主线程忙碌时处理部分合成动画。页面首屏不能简单等同于 load 事件,建议同时关注 FCP、LCP、INP 和 CLS。

十、HTML 中的子资源请求

解析 HTML 时,浏览器会发现样式表、脚本、图片、字体、音视频、iframe 和 fetch 请求,并根据优先级、缓存和连接状态安排加载。不同资源的行为不同:

  • 样式表可能阻塞渲染;
  • 经典同步脚本可能暂停 HTML 解析;
  • defer 脚本与模块脚本通常在解析后执行;
  • async 脚本下载完成后尽快执行,不保证顺序;
  • 图片、字体和视频通常可以并行请求,但仍受优先级和网络带宽影响;
  • 跨域资源可能受到 CORS、Timing-Allow-Origin 和凭证策略限制。

资源缓存可能来自内存缓存、磁盘缓存、Service Worker、CDN 或代理。Service Worker 能拦截 fetch,但它不是“永远优先于所有缓存”的简单固定层;最终结果取决于 fetch 事件、缓存策略和浏览器实现。

十一、连接关闭和页面后续活动

HTTP/1.1 持久连接、HTTP/2 多路复用和 HTTP/3 QUIC 都允许连接在一次响应后继续使用。只有服务器、客户端、超时、网络变化或协议策略要求时,连接才会关闭。

TCP 主动关闭时通常经过:

  1. 一方发送 FIN
  2. 另一方确认;
  3. 另一方完成剩余发送后发送 FIN
  4. 第一方确认并进入 TIME_WAIT 等状态。

TCP 四次挥手示意

“总是四个独立报文”不是绝对保证,因为 ACK 和 FIN 在某些时机可以合并。主动关闭方进入 TIME_WAIT 是为了让旧报文过期并有机会重传最后的确认,时长由 TCP 实现和最大报文生存时间相关,不应在前端代码中硬编码。

十二、把链路用于性能分析

可以使用浏览器 API 观察各阶段,而不是只背诵流程:

const navigation = performance.getEntriesByType('navigation')[0]

if (navigation) {
  console.table({
    dns: navigation.domainLookupEnd - navigation.domainLookupStart,
    connect: navigation.connectEnd - navigation.connectStart,
    ttfb: navigation.responseStart - navigation.startTime,
    domContentLoaded: navigation.domContentLoadedEventEnd,
    load: navigation.loadEventEnd
  })
}

const resources = performance.getEntriesByType('resource')
console.table(resources.map(resource => ({
  name: resource.name,
  initiatorType: resource.initiatorType,
  duration: resource.duration,
  transferSize: resource.transferSize
})))

真实监控还应区分:

  • 首次导航、刷新、前进后退和 BFCache 恢复;
  • 内存缓存、磁盘缓存、Service Worker 和网络请求;
  • TCP/TLS、QUIC、连接复用和代理;
  • 文档导航与 SPA 路由切换;
  • 页面性能与真实用户设备、网络和地域。

总结

  • URL 解析、缓存、HSTS、DNS、连接、HTTP、服务器、文档提交和渲染是相互衔接但并非固定线性的阶段;
  • URL 片段通常不发送给服务器,HSTS 升级也不代表跳过 DNS;
  • DNS 不只有 UDP,HTTP 也不只有 HTTP/1.1 + TCP;HTTP/3 使用 QUIC/UDP;
  • 三次 TCP 握手、TLS 握手和四次挥手是协议模型,不应写成所有请求的固定耗时;
  • 301/302/303/307/308 的选择应看资源迁移和请求方法,不要用“SEO 一定更好”替代语义;
  • 现代浏览器通过多进程、多线程、流式解析、增量布局、栅格化和合成完成页面显示;
  • 性能优化应使用 Navigation Timing、Resource Timing、PerformanceObserver 和 Web Vitals 验证真实结果。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS