推荐方案是直接使用 Go 标准库的 gzip.Handler,作为最外层包装器调用 http.ListenAndServe(":3000", gzip.Handler(app)),它自动处理 header 和流式响应,且仅压缩满足状态码 2xx/3xx、Content-Length >1024 或未知、Content-Type 可压缩的响应。

Go 标准库 gzip.Handler 是唯一推荐方案
Fiber 本身不提供响应体压缩中间件,也不建议用第三方封装或手动 gzip.NewWriter。直接复用 Go 标准库的 gzip.Handler,它是经过生产验证、零额外依赖、自动处理 header 和流式响应的可靠实现。
关键点在于:它必须作为最外层包装器,不能嵌套在任何 Fiber 中间件(如 Logger、Recover)内部,否则会劫持失败——现象是响应头出现 Content-Encoding: gzip,但 body 没压缩,浏览器报 ERR_CONTENT_DECODING_FAILED。
正确写法只有一种:
http.ListenAndServe(":3000", gzip.Handler(app))
注意:app 是你调用 fiber.New() 后注册完所有路由的实例,不是 fiber.Default() —— 后者自带日志中间件,会破坏 gzip.Handler 对原始 ResponseWriter 的接管。
立即学习“go语言免费学习笔记(深入)”;
哪些响应会被跳过压缩?三个硬条件必须同时满足
gzip.Handler 不是“有 Accept-Encoding 就压”,而是严格按以下三条件过滤:
- 状态码必须是
2xx或3xx(404、500等错误响应默认不压) -
Content-Length未知,或已知且 > 1024 字节(小响应压了反而更大,还白耗 CPU) -
Content-Type属于可压缩类型,例如text/html、application/json、text/css;而image/png、application/octet-stream会被跳过
常见踩坑:c.JSON() 返回 JSON 却没设 Content-Type,或误设为 text/plain,导致不进压缩逻辑。调试时用 curl -I -H "Accept-Encoding: gzip" http://localhost:3000/api/users 看响应头是否含 Content-Encoding: gzip 最直接。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
想跳过健康检查路径或调压缩级别?得自己写 wrapper
gzip.Handler 不接受任何参数,没法加白名单、调 level、排除 /healthz。这时候必须抄它源码逻辑,手动包装 ResponseWriter:
核心步骤是:
- 在中间件里判断
c.Path()是否需跳过(如strings.HasPrefix(c.Path(), "/healthz")) - 若不跳过,用
gzip.NewWriterLevel(w, gzip.BestSpeed)包装c.Response().Writer - 确保在
c.Send()前调用w.(*gzip.Writer).Close(),否则 body 不刷出
别用 gzip.Compressor{}(旧版 API),新版必须实现 io.WriteCloser 并显式 close。
静态文件也要包进去,否则 .js/.css 不压缩
如果你用 app.Static("/static", "./public") 提供前端资源,gzip.Handler 必须包裹整个 app 实例,而不是只包路由 handler。否则 /static/main.js 这类请求根本不会经过 gzip.Handler。
等效于把 http.FileServer 直接传给 gzip.Handler,Fiber 的 Static 底层就是它。漏包 = 静态资源裸奔,首屏加载慢 30%+。
真正容易被忽略的是:上线前没验证所有路径(尤其是带 query 的 API、带重定向的登录回调、/favicon.ico)是否都进了压缩链路。一个没包进去的路径,就可能让整页 JS/CSS 失去压缩收益。


















