Go 的 HTTP 压缩必须显式配置,gzip.Handler 必须置于最外层以直接操作 ResponseWriter,否则因中间件封装导致 header 与 body 不匹配;它仅压缩满足状态码、大小和 MIME 类型三条件的响应,且不处理请求体解压或流式响应。

Go 默认不压缩 HTTP 请求或响应,必须显式介入;服务端响应压缩靠 gzip.Handler 或 handlers.CompressHandler,但请求体解压需手动处理,且二者不能混用。
为什么 gzip.Handler 必须包在最外层
gzip.Handler 不是普通中间件,它依赖直接操作原始 http.ResponseWriter 的 Write() 和 WriteHeader()。一旦被 chi.Router、日志、CORS 或认证中间件包裹在内层,底层 writer 就会被封装(比如加了 header 记录逻辑),导致 gzip.Handler 无法劫持写入行为——结果就是响应头里有 Content-Encoding: gzip,但 body 没压缩,浏览器报 ERR_CONTENT_DECODING_FAILED。
正确写法只有一种:
http.ListenAndServe(":8080", gzip.Handler(myMux))
- 静态文件服务(如
http.FileServer)也必须包进去,否则.js、.css不会压缩 - 若你用的是
chi.Router,得确保gzip.Handler是最终包装者,而不是chi.Chain().Handler(myMux)的一部分 - 任何提前调用
w.WriteHeader(200)的 handler 都会让 header 锁定,后续无法补Content-Encoding,此时curl -I -H "Accept-Encoding: gzip"看不到该 header
哪些响应会被 gzip.Handler 跳过
它不是“有 Accept-Encoding: gzip 就压”,而是三条件同时满足才触发:
立即学习“go语言免费学习笔记(深入)”;
- 状态码是
2xx或3xx(4xx/5xx默认跳过,避免暴露调试信息) -
Content-Length未知,或已知且大于1024字节(小响应压缩收益低,白耗 CPU) -
Content-Type属于可压缩类型,例如text/html、application/json、text/css;而image/png、application/octet-stream这类本身已压缩的类型会被跳过
常见踩坑:
- 返回 JSON 却设了
text/plain,导致 MIME 类型不匹配,压缩失效 - handler 中提前调了
w.WriteHeader(200),header 锁定后gzip.Handler无法注入Content-Encoding
如何支持请求体解压或自定义压缩策略
gzip.Handler 和 handlers.CompressHandler 都只处理响应压缩,不碰请求体。如果你需要接收并解压 Content-Encoding: gzip 的请求体(比如客户端上传压缩 JSON),必须手动做:
- 检查
r.Header.Get("Content-Encoding") == "gzip" - 用
gzip.NewReader(r.Body)包装原始Body,再传给你的解析逻辑 - 注意:别对已解压的
Body二次解压,否则 panicgzip: invalid header
想跳过健康检查路径、改压缩级别、或支持 br/zstd?gzip.Handler 不支持任何参数。这时只能自己写 wrapper:
- 实现完整
http.ResponseWriter接口,重写WriteHeader()检查Accept-Encoding -
Write()第一次调用时才初始化*gzip.Writer,避免对304/204响应无效包装 - 务必在 handler 返回前调
gz.Close(),否则缺少 CRC32 和 ISIZE 尾部,解压端报gzip: invalid checksum
流式响应(SSE、chunked)为什么不能直接套 gzip
HTTP 流式响应要求低延迟分块输出,而 gzip.Writer 需要完整输入或显式 Flush() 才能产出有效压缩块。强行压缩会导致:
- 首屏延迟激增(等 buffer 满才发)
- 连接被客户端中断(超时或解码失败)
- 浏览器报
net::ERR_INCOMPLETE_CHUNKED_ENCODING
解决方式只有两个:
- 对流式接口禁用压缩(比如路径含
/events或text/event-streamMIME) - 或改用
Transfer-Encoding: chunked+ 自定义压缩帧协议(不推荐,复杂度陡增)
真正容易被忽略的是:压缩和反向代理(如 Nginx)共存时,若两者都启用 gzip,会出现双重压缩或 header 冲突;生产环境建议只在 Go 服务层或 Nginx 层选其一启用,且确保 Vary: Accept-Encoding 正确设置。


















