惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
WordPress大学
WordPress大学
小众软件
小众软件
Cloudbric
Cloudbric
AWS News Blog
AWS News Blog
腾讯CDC
量子位
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Vulnerabilities – Threatpost
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
Jina AI
Jina AI
Attack and Defense Labs
Attack and Defense Labs
S
SegmentFault 最新的问题
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
Last Week in AI
Last Week in AI
博客园 - 【当耐特】
Google Online Security Blog
Google Online Security Blog
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
罗磊的独立博客
L
LINUX DO - 最新话题
博客园 - Franky
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
The Last Watchdog
The Last Watchdog
J
Java Code Geeks
AI
AI
C
Cisco Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cyber Attacks, Cyber Crime and Cyber Security
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
I
Intezer
S
Securelist

竹林里有冰的博客

Nuxt SSG 博客的尾斜杠到底怎么加? | 竹林里有冰的博客 小米 Xiaomi Book Pro 14 (Ultra X7) Linux 兼容性实测 | 竹林里有冰的博客 国内(大陆)版小米 FCM 熄屏断连:Rootless 环境下的尝试与可能的解决方案 | 竹林里有冰的博客 我没法访问 dl.google.com —— 记一次 TUN 下的网络 debug | 竹林里有冰的博客 Vercel 的缓存控制,你注意过吗? | 竹林里有冰的博客 小记 —— Caddy 在 Layer 4 上的流量代理实践 | 竹林里有冰的博客 你的域名后缀拖慢你的网站速度了嘛?——再谈 DNS 冷启动 | 竹林里有冰的博客 DNS 冷启动:小型站点的“西西弗斯之石” | 竹林里有冰的博客 Nuxt Content v3 中数组字段的筛选困境与性能优化 | 竹林里有冰的博客 后 OCSP 时代,浏览器如何应对证书吊销新挑战 | 竹林里有冰的博客 初试 Github Action Self-hosted Runner,想说爱你不容易 | 竹林里有冰的博客 DNS 解析延迟毁了我的图床优化 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(下):告别 DOM 操作,拥抱 AST 与函数式渲染 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(上):从暴力刷新、分块更新到 Morphdom 的华丽变身 | 竹林里有冰的博客 node-sass 迁移至 dart-sass 踩坑实录 | 竹林里有冰的博客 前端中的量子力学——一打开 F12 就消失的 Bug | 竹林里有冰的博客 2025 年,如何为 web 页面上展示的视频选择合适的压缩算法? | 竹林里有冰的博客 el-image 和 el-table 怎么就打架了?Stacking Context 是什么? | 竹林里有冰的博客 2025年,前端如何使用 JS 将文本复制到剪切板? | 竹林里有冰的博客 ssh 拯救世界——通过 ssh 隧道在内网服务器执行 APT 更新 | 竹林里有冰的博客 Cudy TR3000 吃鹅(daed)记 | 竹林里有冰的博客 使用 Cloudflare Workers 监控 Fedora Copr 构建状态 | 竹林里有冰的博客 基于 Cloudflare Workers 实现的在线服务状态检测告警系统 | 竹林里有冰的博客 构建部署在 Cloudflare Workers 上的 TG Bot | 竹林里有冰的博客 2024年,Firefox 是唯一还在坚持执行在线的 SSL 证书吊销状态检查的主流浏览器 | 竹林里有冰的博客 小爱课程表适配不完全指北——以 ZJUT 本科正方教务系统为例 | 竹林里有冰的博客 将博客从 waline v2 更新到 waline v3 | 竹林里有冰的博客 给家里云装上 Fedora 41 KDE 后,我是如何配置的 | 竹林里有冰的博客 为 Hexo 添加 follow 认证 | 竹林里有冰的博客 使用 GPT 对 waline 的评论进行审查 | 竹林里有冰的博客 基于 JavaScript 的 Hexo Fluid 主题 banner 随机背景图实现 | 竹林里有冰的博客 使用向日葵智能插座 C2 用电记录推算宿舍上次烧水时间 | 竹林里有冰的博客 使用 Caddy 反向代理 dockerhub 需要几步? | 竹林里有冰的博客 将 Rustdesk 中继服务从 Arch Linux 迁移至 Debian | 竹林里有冰的博客 自建图床小记五——费用 | 竹林里有冰的博客 自建图床小记四——上传脚本编写与图片迁移 | 竹林里有冰的博客 自建图床小记三—— SSL 证书的自动更新与部署 | 竹林里有冰的博客 自建图床小记二——使用 Workers 为 R2 构建 Restful API | 竹林里有冰的博客 自建图床小记一——图床架构与 DNS 解析 | 竹林里有冰的博客 在 Linux 下使用 mitmproxy 抓取安卓手机上的 HTTPS 流量 | 竹林里有冰的博客 为中柏 N100 小主机开启来电自启 | 竹林里有冰的博客 我的博客被完整地反向代理,并自动翻译成了繁体中文 | 竹林里有冰的博客 尝试体验 Fedora COPR 中的 allow SSH 功能 | 竹林里有冰的博客 在 Arch Linux 下配置使用 HP Laser 103w 打印机无线打印 | 竹林里有冰的博客 使用动态公网 ip + ddns 实现 rustdesk 的 ip 直连 | 竹林里有冰的博客 使用 Windows 虚拟机运行虚拟专用网客户端为 Linux 提供内网环境 | 竹林里有冰的博客 以 Archlinux 中 makepkg 的方式打开 rpmbuild | 竹林里有冰的博客 使用 Github Action 更新用于 rpm 打包的 spec 文件 | 竹林里有冰的博客 使用 Python 生成甘特图(Gantt Chart) | 竹林里有冰的博客 uniapp 中的图片预加载 | 竹林里有冰的博客 小记 - 尝试拼凑出 apt 仓库中的 deb 包下载地址 | 竹林里有冰的博客 在 Linux 下使用 mitmproxy 抓取 HTTPS 流量 | 竹林里有冰的博客 如何使用 docker 部署 onemanager | 竹林里有冰的博客 crontab 中简单的@语法糖 | 竹林里有冰的博客 备份 umami 数据库,并使用 TG Bot 保存 dump 文件 | 竹林里有冰的博客 在 JavaScript 中,箭头函数中的 this 指针到底指向哪里? | 竹林里有冰的博客 结合 Vue.js 与 php 完成的 web 期末大作业,讲讲前后端分离站点开发与部署中可能遇到的 CORS 跨域问题 | 竹林里有冰的博客 vuejs、php、caddy 与 docker —— web 期末大作业上云部署 | 竹林里有冰的博客 【翻译】使用 PHP 构建简单的 REST API | 竹林里有冰的博客 在 Hexo Fluid 主题中使用霞鹜文楷 | 竹林里有冰的博客 【翻译】GLWTPL——祝你好运开源许可证 | 竹林里有冰的博客 通过巴法云将向日葵智能插座接入米家,实现小爱同学远程控制 | 竹林里有冰的博客 使用 Root 后的安卓手机获取向日葵智能插座 C2 的开关 api | 竹林里有冰的博客 创建 b23.tv 追踪参数移除 bot | 竹林里有冰的博客 jinja2 中如何优雅地实现换行 | 竹林里有冰的博客 手动指定 python-selenium 的 driver path 以解决在中国大陆网络环境下启动卡住的问题 | 竹林里有冰的博客 从零开始的静态网页部署(到个人云服务器) | 竹林里有冰的博客 在运行OpenWRT的N1盒子上部署 QQBot | 竹林里有冰的博客 在浙工大宿舍使用路由器连接移动网络(校园网) | 竹林里有冰的博客 为红米 Redmi AC2100 路由器刷入 Padavan | 竹林里有冰的博客 Azure 教育订阅申请时遇到的麻烦 | 竹林里有冰的博客 执行 repo sync 后将 git-lfs 中的资源文件 checkout | 竹林里有冰的博客 隐式转发——骚套路建站方案 | 竹林里有冰的博客 在 vps 上配合 caddy 部署 siteproxy | 竹林里有冰的博客 onedrive(by abraunegg) —— 一个 Linux 下的开源 OneDrive 客户端(cli) | 竹林里有冰的博客 【翻译】关于2022年11月的事件的一些话[Z-Library] | 竹林里有冰的博客 【已过期】使用 vercel+supabase 免费部署 umami | 竹林里有冰的博客 我的博客部署方案 | 竹林里有冰的博客 使用 VirtScreen 将 Pad 作为副屏 | 竹林里有冰的博客 在 Archlinux 下使用 l2tp 协议连接校园网 | 竹林里有冰的博客 为 Element 添加自己喜欢的贴纸 | 竹林里有冰的博客 nodejs16:是我配不上 openssl 3 咯? | 竹林里有冰的博客 如何拯救失声的 hollywood | 竹林里有冰的博客 处理 fcitx5 的文字候选框在 tg 客户端上闪烁的问题 | 竹林里有冰的博客 使用caddy反向代理维基百科中文站点 | 竹林里有冰的博客 创建一个本地的 Fedora 镜像源 | 竹林里有冰的博客 好软推荐——FastOCR | 竹林里有冰的博客 抛弃PicGo,直接使用curl将图片上传到LskyPro | 竹林里有冰的博客 使用 Github Action 跑 rpmbuild | 竹林里有冰的博客 如何打出一个「-git」的rpm包 | 竹林里有冰的博客 雪藏在开源镜像站点中的那些常用却不为人知的软件 | 竹林里有冰的博客 在Fedora搭建jekyll环境——dnf module | 竹林里有冰的博客 pacman更新时遇到「GPGME 错误:无数据」 | 竹林里有冰的博客 Cutefish的前世今生 | 竹林里有冰的博客 wolai再打包遇到的问题--electron应用的dev判断机制 | 竹林里有冰的博客 Typora与我 | 竹林里有冰的博客 我是来吹CloudflareMirrors的 | 竹林里有冰的博客 deepin-elf-verify究竟是何物? | 竹林里有冰的博客 【翻译】请别再使用主题装饰我们的软件 | 竹林里有冰的博客 Waydroid on KDE 初体验 | 竹林里有冰的博客
HTTP/2 Server Push 已事实性“死亡”,我很怀念它 | 竹林里有冰的博客
竹林里有冰 · 2025-11-05 · via 竹林里有冰的博客

我最近一阵子在重构我的博客,恰巧之前一阵子准备秋招的时候背八股时看到了 HTTP/2 的服务端推送,于是便尝试在部署阶段为我的博客配置好 HTTP/2 的服务端推送,试图以此来进一步优化首屏加载速度。

如下图,在传统的 HTTP/1.1 中,浏览器会先下载 index.html 并完成第一轮解析,然后再从解析出的数据中拿到 css/js 资源的 url,再进行第二轮请求,在 tcp/tls 连接建立后最小需要两个 RTT 才能取回完整渲染页面所需的资源。

sequenceDiagram
    participant Browser
    participant Server

    Browser->>Server: GET /index.html
    Server-->>Browser: 200 OK + HTML
    Browser->>Server: GET /style.css
    Browser->>Server: GET /app.js
    Server-->>Browser: 200 OK + CSS
    Server-->>Browser: 200 OK + JS

    Note over Browser: 浏览器必须等 HTML 下载并解析后<br/>才能发起后续请求,增加往返延迟 (RTT)

而在 HTTP/2 的设想中,流程则是像下面这张图一样。当浏览器请求 index.html 时,服务端可以顺带将 css/js 资源一起推送给客户端,这样在 tcp/tls 连接建立后最小只需要一个 RTT 就可以将页面渲染所需的资源取回。

sequenceDiagram
    participant Browser
    participant Server

    Browser->>Server: GET /index.html
    Server-->>Browser: 200 OK + HTML
    Server-->>Browser: PUSH_PROMISE /style.css
    Server-->>Browser: PUSH_PROMISE /app.js
    Server-->>Browser: (推送) style.css + app.js 内容

    Note over Browser: 浏览器收到资源前置推送<br/>减少请求轮次与首屏延迟

为了在 HTTP/1.1 中尽可能减少后续的请求,前端开发者尝试了非常多的优化手段,正如 Sukka 在《静态资源递送优化:HTTP/2 和 Server Push》一文中所讲:

关键资源、关键渲染路径、关键请求链的概念诞生已久,异步加载资源的概念可谓是老生常谈:懒加载图片、视频、iframe,乃至懒加载 CSS、JS、DOM,懒执行函数。但是,关键资源递送的思路却依然没有多少改变。

HTTP/2 的 Server Push 创造了新的资源递送思路,CSS/JS 等资源不用随着 html 一起递送也能在一个 RTT 内被传送到客户端,而这一部分资源可以被浏览器缓存起来,不被 html 那较短的 TTL 所限制。

初步方案#

既然理清了 HTTP/2 服务端推送的优势,于是准备着手优化。我的博客是纯静态的,通过 DNS 进行境内外分流:境内流量会访问到 DMIT 一台带有 cmin2/9929 网络优化的 vps 上,通过 caddy 提供服务;境外流量则是直接打到 vercel,借助 Amazon 的 CDN 为全球网络提供边缘加速。网络架构大概是下面这个样子:

graph TD
    A[博客访客] --> B[发起DNS解析请求]
    B --> C[DNSPod 服务]
    C -->|境内访客:智能分流| D[网络优化 VPS]
    C -->|境外访客:智能分流| E[Vercel 平台]
    D --> F[Caddy]
    F --> G[返回博客内容给境内访客]
    E --> H[返回博客内容给境外访客]

Caddy 可以通过 http.handlers.push 模块实现 HTTP/2 的服务端推送,在 Caddyfile 中我们可以编写简单的推送逻辑,这没问题;vercel 平台则没有给开发者提供 HTTP/2 服务端推送的配置项,但好在我是静态博客,对平台依赖性不强,考虑迁移到 Cloudflare Workers,五年前就有开发者实现了

客户端的支持情况#

历史上主流浏览器引擎(Chrome/Chromium、Firefox、Edge、Safari)曾普遍支持服务器推送技术。

2020 年 11 月,谷歌宣布计划在其 Chrome 浏览器的 HTTP/2 及 gQUIC(后发展为HTTP/3)实现中移除服务器推送功能

2022 年 10 月,谷歌宣布计划从Chrome浏览器中移除服务器推送功能,指出该扩展在实际应用中性能不佳、使用率低且存在更优替代方案。Chrome 106 成为首个默认禁用服务器推送的版本。

2024 年 10 月 29 日,Mozilla 发布了 Firefox 132版本,因“与多个网站存在兼容性问题”移除了对HTTP/2服务器推送功能的支持。

至此,主流浏览器对 HTTP/2 服务端推送(Server Push)的支持已全部终结。从最初被视为“减少往返延迟、优化首屏加载”的创新特性,到最终被全面弃用,HTTP/2 推送的生命周期不过短短数年,成为 Web 性能优化历史上的一次重要实验。

替代方案#

1. HTTP 103 Early Hints#

103 Early Hints 是对服务端推送最直接的“继任者”。它是一个信息性的 HTTP 状态码 (Informational Response),允许服务器在生成完整的 HTML 响应(例如,状态码为 200 OK)之前,先发送一个带有 Link 头部的“早期提示”响应。

这个 Link 头部可以告诉浏览器:“嘿,我还在准备主菜(HTML),但你可以先去把配菜(CSS、JS)准备好”。这样,浏览器就能利用服务器的“思考时间”提前开始下载关键资源或预热到所需源的连接,从而显著缩短首屏渲染时间。

与服务端推送的对比:

  • 决策权在客户端:Early Hints 只是“提示”,浏览器可以根据自身缓存情况、网络状况等因素决定是否采纳该提示。这就解决了服务端推送最大的痛点——服务器无法知晓客户端缓存而导致推送冗余资源。
  • 兼容性更好:它是一种更轻量、更易于中间代理服务器理解和传递的机制。

103 Early Hints 对动态博客很有意义,在后端进行计算之前先把需要的资源通过 103 响应告知浏览器,让浏览器先取回其他资源,再等待后端返回最终的 html;而对于我这种产物都是预构建好的静态博客,完全没有任何意义,网关有那个发 103 响应的闲工夫完全可以把 html 直接发过去了。

2. 资源提示(Resource Hints): Preload & Prefetch#

早在服务端推送被弃用前,通过 <link>标签实现的资源提示就已经是前端性能优化的常用手段。它们将资源加载的提示声明在 HTML 中,由浏览器主导整个过程。

  • <link rel="preload">: 用于告诉浏览器当前页面必定会用到的资源,请以高优先级立即开始加载,但加载后不执行。比如,隐藏在 CSS 深处的字体文件或由 JS 动态加载的首屏图片。通过 Preload,可以确保这些关键资源能尽早被发现和下载,避免渲染阻塞。
  • <link rel="prefetch">: 用于告诉浏览器用户在未来可能访问的页面或用到的资源,请在浏览器空闲时以低优先级在后台下载。例如,在文章列表页 prefetch 用户最可能点击的文章页面的资源,从而实现近乎“秒开”的跳转体验。

Preload 和 Prefetch 将资源加载的控制权完全交给了开发者和浏览器,通过声明式的方式精细化管理资源加载的优先级和时机,是目前最成熟、应用最广泛的资源预加载方案,但仍然逃不过 2 RTT 的魔咒

尾声:写给一个理想主义者的挽歌#

写到最后,我终究是没能为我的博客配上 HTTP/2 服务端推送。

HTTP/2 Server Push 已事实性“死亡”,我很怀念它。

在一个理想模型里,当浏览器请求 HTML 时,服务器顺手将渲染所需的 CSS 和 JS 一并推来,将原本至少两次的往返(RTT)干脆利落地压缩为一次。这是一个如此直接、如此漂亮的解决方案,几乎是前端工程师面对首屏渲染延迟问题时梦寐以求的“银弹”。它背后蕴含的是一种雄心勃勃的魄力:试图由服务端一次性地、彻底地解决“关键请求链”的延迟问题。

但 Web 的世界终究不是一个理想的实验室。它充满了缓存、重复访问的用户、以及形形色色的网络环境。

服务端推送最大的魅力,在于它的“主动”,而它最大的遗憾,也恰恰源于这份“主动”。它无法知晓浏览器缓存中是否早已静静躺着那个它正准备满腔热情推送的 style.css 文件。为了那一小部分首次访问用户的极致体验,却可能要以浪费更多再次访问用户的宝贵带宽为代价。

Web 的演进最终选择了一条更稳妥、更具协作精神的道路。它将决策权交还给了最了解情况的浏览器,整个交互从服务器的“我推送给你”,变成了服务器的“我建议你拿”,再由浏览器自己定夺。这或许不够浪漫,不够极致,但它更普适,也更健壮。

所以,我依然会怀念那个雄心勃勃的 Server Push。它代表了一种对极致性能的纯粹追求,一种美好的技术理想主义。尽管它已悄然淡出历史舞台,但它所指向的那个关于“速度”的梦想,早已被 103 Early Hints 和 preload 以一种更成熟、更懂得权衡的方式继承了下来。

参见#