gzip是http场景唯一稳妥选择,因其被浏览器和cdn广泛支持;zstd和snappy需手动封装且仅适用于内网或私有协议;gzip.handler必须置于最外层以接管responsewriter,否则响应头设gzip但body未压缩导致解压失败;它仅在状态码2xx/3xx、content-length>1024或未知、content-type可压缩时生效。

Go 服务端响应压缩不能只看“哪个算法快”,得先看客户端认不认、中间件压不压得进、小数据压了反而变大——gzip 是 HTTP 场景唯一稳妥选择,zstd 和 snappy 必须自己封装且仅适用于内网或私有协议。
HTTP 响应里 gzip.Handler 为什么必须放最外层
因为 gzip.Handler 不是装饰器,它要直接接管原始 http.ResponseWriter 的 Write() 和 WriteHeader() 调用。一旦被日志、CORS、Auth 等中间件包在内层,底层 writer 就被封装成带状态的 wrapper,gzip.Handler 就劫持失败——结果是响应头写了 Content-Encoding: gzip,但 body 没压缩,浏览器报 ERR_CONTENT_DECODING_FAILED。
- 正确写法只有一种:
http.ListenAndServe(":8080", gzip.Handler(myMux)) - 静态文件服务(如
http.FileServer)也得包进去,否则.js/.css不压缩 - 用
chi.Router或自定义链时,确保gzip.Handler是最终包装者
哪些响应会被 gzip.Handler 自动跳过
它不是“有 Accept-Encoding: gzip 就压”,而是硬性三条件同时满足才触发:
- 状态码必须是
2xx或3xx(4xx/5xx错误页默认不压) -
Content-Length未知,或已知且> 1024字节(小响应压缩后常更大,还白耗 CPU) -
Content-Type属于可压缩类型,例如text/html、application/json、text/css;而image/png、application/octet-stream这类本身已压缩的类型会被跳过
常见踩坑:return JSON 却设了 text/plain,或 handler 中提前调了 w.WriteHeader(200) 导致 header 锁定,后续没法补 Content-Encoding —— 此时 curl -I -H "Accept-Encoding: gzip" 看不到该 header,就是没进压缩逻辑。
想用 zstd 或 snappy?别指望标准库自动支持
浏览器和绝大多数 CDN 只认 gzip 和 br。Accept-Encoding: zstd 在 Chrome/Firefox 里根本不会发,curl 默认也不带,所以服务端主动协商的前提是客户端明确声明——这在公网 HTTP 场景基本不可行。
-
zstd必须双注册:grpc.RPCCompressor和grpc.RPCDecompressor缺一不可,漏一个就解压失败;别用zstd.Compressor{}(旧版 API),新版要实现encoding.Compressor接口 -
snappy在 Kafka/NATS/Sarama 中默认可用,但 framing 模式要对齐;HTTP 场景需手动封装ResponseWriter,且每个请求必须新建压缩器实例,防并发冲突 - 小数据(如 100 字节 JSON)用
gzip压完常变大(头部固定开销 18–30 字节),zstdheader 更小(1KB 消息下仅 4 字节),但前提是客户端能解
压缩前必须预判:小数据负增益、二进制白忙活
小于 200 字节的 JSON,gzip 压完大概率变大;大于 1MB 的 base64 图片,压缩率常低于 2%,CPU 白花。实测 2KB protobuf 消息,gzip 体积仅减 12%,耗时却达 80–150μs;snappy 同样输入,体积减 28%,耗时稳定在 ~5μs。
- 高频低延迟服务优先选
snappy(解压极快) - 想兼顾压缩率和速度,
zstd是当前最稳的选择,但必须手动注册,否则直接 panic:compressor not registered - 内网 RPC 或私有协议可大胆上
zstd,用zstd.WithDecoderAllowInsecure关闭校验(仅可信环境)提升 5% 解压吞吐
真正容易被忽略的是:所有手动封装的压缩器(无论 zstd、lz4 还是 snappy),都必须在 handler 返回前调 Close(),否则流缺少尾部校验(CRC32/ISIZE),客户端解压失败报 gzip: invalid checksum 或 invalid compressed data--format violated。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











