
Go 的 http.Server 默认不支持 HTTP/2 服务端推送
HTTP/2 Push 在 Go 标准库中从未被实现过,http.Pusher 接口虽存在,但自 Go 1.8 引入起就始终是空实现(panic 或直接返回 ErrNotSupported)。你调用 resp.Push() 时大概率会看到:http: server doesn't support HTTP/2 push。
这不是配置问题,也不是 TLS 没开好——是标准库压根没写这部分逻辑。官方明确表示:Push 有设计缺陷、易被滥用、现代前端工具链(如 HTTP/2 preload + cache 策略)已能更好替代它。
- Go 1.8–1.22 所有版本均无服务端推送能力
-
http.Pusher是个兼容性占位符,不是功能开关 - 即使强制启用 HTTP/2(如用
http2.ConfigureServer),Push()仍不可用
想“模拟”推送效果?用 Link 响应头 + preload
真正能落地的方案是放弃 Push,改用标准、轻量、客户端自主控制的 Link 头。浏览器收到后会预加载资源,行为接近 Push,但无队头阻塞风险,也无需服务端维护推送状态。
注意:Link 必须在主响应头中发出(不能流式写入后加),且路径需为绝对 URL 或以 / 开头的相对路径(浏览器按当前文档 base 解析)。
立即学习“go语言免费学习笔记(深入)”;
- 示例:
w.Header().Set("Link", `<style.css>; rel=preload; as=style, <logo.svg>; rel=preload; as=image`)</logo.svg></style.css> - 只对支持
preload的资源类型有效(script、style、image、font等) - 避免预加载过大或非关键资源,否则反而拖慢首屏
- 不依赖 TLS:HTTP/1.1 下也能生效(只要客户端支持 preload)
别碰第三方 HTTP/2 推送库,比如 golang.org/x/net/http2 的实验分支
有人翻出旧版社区 PR 或 fork 的 http2 实现,试图手动注入 PUSH_PROMISE 帧。这类代码要么无法编译(API 已废弃),要么在 Go 1.16+ 后彻底失效,还可能破坏 TLS 连接复用或触发 connection error: PROTOCOL_ERROR。
根本原因:Go 的 http2.Framer 和 http2.serverConn 是内部结构,未暴露推送帧构造接口;强行反射或 patch 会导致运行时不稳定,且无法通过 go vet 和安全扫描。
- 所有声称“支持 Go HTTP/2 Push”的开源库,实际都只是包装了
Link头 - 没有一个能在生产环境长期稳定工作
- 升级 Go 版本后,这类 hack 几乎必然崩溃
真需要资源协同调度?转向应用层主动策略
与其纠结协议层推送,不如把逻辑提到业务侧:例如模板渲染时提前收集依赖资源,统一生成 Link;或用 Service Worker 缓存关键静态资源,首次访问后即离线可用。
更进一步,用 http.ResponseController(Go 1.22+)精细控制流式响应生命周期,配合 Flush() 和 SetWriteDeadline() 避免长连接僵死——这比模拟 Push 更可控、更符合 Go 的显式设计哲学。
- 静态资源走 CDN + Cache-Control,动态内容用 ETag 或 Vary
- 首屏 HTML 内联关键 CSS,延迟加载非核心 JS
- 用
net/http/pprof观察真实连接耗时,而不是假设 Push 能省多少 RTT
Link 头和现代缓存策略更省心。










