ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Traefik 官方 compress 中间件完全指南:Gzip / Brotli / Zstandard 响应压缩配置与源码解析

2026/9/8 20:48:32 拓冰建站 浏览量
Traefik 官方 compress 中间件完全指南:Gzip / Brotli / Zstandard 响应压缩配置与源码解析 Traefik 官方 compress 中间件完全指南Gzip / Brotli / Zstandard 响应压缩配置与源码解析【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefikcompress是 TraefikThe Cloud Native Application Proxy内置的一款 HTTP 响应压缩中间件位于 HTTP 路由与后端服务之间在响应发送给客户端之前依据Accept-Encoding协商并选择Gzip、Brotli、Zstandard三种编码之一进行压缩。本文以仓库中的官方文档 compress.md 为骨架结合其源码实现 pkg/middlewares/compress 展开成文读完你可以掌握如何在 YAML / TOML / Docker Labels / Swarm Tags / Kubernetes CRD 五种配置形态下启用压缩如何通过excludedContentTypes、includedContentTypes、minResponseBodyBytes、encodings、defaultEncoding五个选项精准控制压缩行为以及压缩中间件在何时压缩、何时跳过、如何协商编码的完整判定逻辑。一、中间件职责与支持能力compress中间件会对后端返回的响应体进行压缩后再发给客户端。在 Traefik v3 中它原生支持三种压缩算法Gzip编码名gzipBrotli编码名brZstandard编码名zstd三种算法的默认优先级为gzip、br、zstd即默认配置下gzip具有最高优先级。源码层面的默认值定义在两处pkg/middlewares/compress/compress.go 第 24 行var defaultSupportedEncodings []string{gzipName, brotliName, zstdName}pkg/config/dynamic/middlewares.go 第 209-211 行Compress.SetDefaults()将Encodings默认设为[]string{gzip, br, zstd}实际编码由三个成熟的开源库完成gzip 走github.com/klauspost/compress/gzhttpBrotli 走github.com/andybalholm/brotliZstandard 走github.com/klauspost/compress/zstd见 compress.go 第 11-13 行的 import。二、五种配置形态的启用示例压缩中间件属于 HTTP 层的动态配置middleware先创建再挂载到 router 上才会真正生效。下面是官方文档给出的五种完全等价的最小启用配置。2.1 结构化 YAML文件 Provider# Enable compression http: middlewares: test-compress: compress: {}2.2 结构化 TOML文件 Provider# Enable compression [http.middlewares] [http.middlewares.test-compress.compress]2.3 Docker / Swarm Labels# Enable compression labels: - traefik.http.middlewares.test-compress.compresstrue2.4 Docker / Swarm TagsJSON// Enable compression { //... Tags: [ traefik.http.middlewares.test-compress.compresstrue ] }2.5 Kubernetes CRDKubernetes 场景使用traefik.io/v1alpha1的Middleware资源# Enable compression apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: test-compress spec: compress: {}上述任意一种配置都能在 pkg/server/middleware/middlewares.go 第 163-171 行的构建逻辑中被识别当检测到config.Compress ! nil时调用compress.New(ctx, next, *config.Compress, middlewareName)生成中间件实例。之后把它挂到对应路由router上例如文件动态配置中http: routers: my-router: rule: Host(example.com) middlewares: - test-compress service: my-service三、配置选项详解官方文档将配置项汇总如下本节逐一给出实现层面解读。FieldDescriptionDefaultRequiredexcludedContentTypesList of content types to compare theContent-Typeheader of the incoming requests and responses before compressing. The responses with content types defined inexcludedContentTypesare not compressed. Content types are compared in a case-insensitive, whitespace-ignored manner.TheexcludedContentTypesandincludedContentTypesoptions are mutually exclusive.NodefaultEncodingspecifies the default encoding if theAccept-Encodingheader is not in the request or contains a wildcard (*).NoencodingsSpecifies the list of supported compression encodings. At least one encoding value must be specified, and valid entries arezstd(Zstandard),br(Brotli), andgzip(Gzip). The order of the list also sets the priority, the top entry has the highest priority.gzip, br, zstdNoincludedContentTypesList of content types to compare theContent-Typeheader of the responses before compressing. The responses with content types defined inincludedContentTypesare compressed. Content types are compared in a case-insensitive, whitespace-ignored manner.TheexcludedContentTypesandincludedContentTypesoptions are mutually exclusive.NominResponseBodyBytesMinimum amount of bytes a response body must have to be compressed. Responses smaller than the specified values willnotbe compressed.1024No仓库内完整字段参照请见 docs/content/reference/dynamic-configuration/file.yaml 第 176-188 行其中Middleware06.compress示范了五个字段的书写位置。3.1 excludedContentTypes 与 includedContentTypes按 Content-Type 收窄压缩范围excludedContentTypes黑名单模式。响应Content-Type命中列表中的类型时不压缩其余类型照常压缩。includedContentTypes白名单模式。只有Content-Type命中列表中的类型才被压缩。两者互斥同时设置会导致中间件创建失败。源码中该互斥校验发生在 compress.go 第 47-49 行if len(conf.ExcludedContentTypes) 0 len(conf.IncludedContentTypes) 0 { return nil, errors.New(excludedContentTypes and includedContentTypes options are mutually exclusive) }两条规则还需要注意两点实现细节比较方式列表项经mime.ParseMediaType解析为标准媒体类型后再比较因此类型名大小写、空白差异会被规范化处理若配置的类型无法被解析为合法 MIME中间件构造会直接报错compress.go 第 52-69 行。application/grpc永远被排除源码在构造 excludes 列表时无条件预置了excludes : []string{application/grpc}compress.go 第 51 行这正是官方文档末尾GRPC applicationapplication/grpcis never compressed声明的实现来源。白名单/黑名单的逐条匹配与included/excluded判定逻辑位于 pkg/middlewares/compress/compression_handler.go 第 234-267 行Brotli/Zstd 路径以及 gzip 封装器的gzhttp.ExceptContentTypes / gzhttp.ContentTypes参数compress.go 第 191-201 行。其中application/grpc也会在 compress.go 第 147 行按请求的Content-Type被提前拦截保证对 gRPC/流式请求一律直通不压缩历史 issue 见源码中引用的 traefik#2576 注释涉及text/event-stream的同类处理。3.2 minResponseBodyBytes小于阈值不压缩只有响应体达到该字节数才会启动压缩过小的响应压缩反而得不偿失。默认值10241 KB定义在 compress.go 第 22 行const defaultMinSize 1024若配置值 0 则覆盖默认第 71-74 行。实现上是边收边判断在 compression_handler.go 第 269-273 行写入的字节先进入缓冲r.buf只有当len(r.buf)len(p) r.minSize时才开始真正压缩并写出压缩流整个请求结束后若缓冲仍未达到阈值则在close()时把缓冲原样不压缩写回第 416-427 行。这意味着响应只要实际足够大就可以中途切换到压缩模式无需后端先完整吐完整个 body。3.3 encodings 与 defaultEncoding协商与降级策略encodings声明中间件支持的编码及其优先级列表顺序即优先级排在最前top entry的优先级最高。可取值仅限zstd、br、gzip且必须至少指定一个——源码 compress.go 第 76-83 行对空列表返回错误at least one encoding must be specified对未知编码返回unsupported encoding: ...。defaultEncoding当请求没有携带Accept-Encoding头、或该头只包含通配符*时使用的兜底编码。若它被配置则必须同时存在于encodings列表中否则构造报错unsupported default encoding: ...compress.go 第 84-86 行。defaultEncoding的两处生效逻辑在 compress.go 第 152-165 行请求头缺失时直接选用默认编码与 acceptencoding.go 第 51-57 行协商结果落到通配符*时返回默认编码未配置默认编码则回退到encodings的第一项。3.4 配置选项的 YAML 完整示例http: middlewares: test-compress: compress: # 只压缩这三类文本内容白名单模式 includedContentTypes: - text/html - text/css - application/json # 响应体小于 512 字节不压缩 minResponseBodyBytes: 512 # 优先 gzip其次 br其次 zstd encodings: - gzip - br - zstd # 客户端未声明 Accept-Encoding 时按 gzip 压缩 defaultEncoding: gzip注意excludedContentTypes与includedContentTypes同一时刻只能出现其一defaultEncoding必须在encodings内。四、压缩激活条件何时压缩、何时不压缩官方文档明确了压缩激活依赖包含但不限于请求的Accept-Encoding头。只有以下条件全部满足时响应才会被压缩请求的Accept-Encoding头中包含gzip和/或br和/或zstd可以带 quality valuesq 值如gzip;q0.8, br也可以出现*。请求头完全缺失时默认不编码除非配置了defaultEncoding此时仍会编码。请求头存在但值为空字符串时压缩被关闭按 RFC 9110表示客户端不想要任何内容编码。响应尚未被压缩过即响应头Content-Encoding还没有被设置。响应的Content-Type不在excludedContentTypes中或命中includedContentTypes白名单。响应体超过minResponseBodyBytes配置的最小字节数默认 1024。4.1 内容协商的内部实现编码选择过程集中在 pkg/middlewares/compress/acceptencoding.go请求头为空串 → 直接返回identity不压缩代码注释引用了 RFC 9110 关于空的 Accept-Encoding 表示不接受任何编码的语义第 27-31 行。解析请求头各编码项及 q 值q0 表示不可接受直接剔除缺省 q 值为 1.0第 62-99 行。先按 q 值降序再按encodings配置的顺序即优先级排序同权重时列表靠前者胜出第 42-49 行。若胜出项是通配符*使用defaultEncoding未配置则用encodings首项第 51-57 行。对应的行为验证在 pkg/middlewares/compress/compress_test.go 第 25-132 行TestNegotiation中有完整覆盖例如客户端Accept-Encoding选中的编码头缺失不压缩gzipgzipbrbrbr;q0.8, gzip;q0.6brq 值更高gzip;q1.0, br;q0.8gzipgzip;q0.8, br;q1.0, zstd;q0.7brzstd;q0.9, br;q0.8, gzip;q0.6zstdgzip, br, zstdq 全相等zstd当前底层库的倾向性结果4.2 其余跳过压缩的路径除上述四条件外还有若干无条件直通场景HEAD 请求req.Method http.MethodHead时直接放行不做任何包装compress.go 第 135-138 行因为 HEAD 没有响应体可压。对应测试为 compress_test.go 第 283 行TestShouldNotCompressHeadRequest。请求本身 Content-Type 为application/grpc或text/event-stream等被排除类型时提前放行compress.go 第 145-150 行避免破坏流式语义。后端已设置Content-Encoding说明上游已经编码中间件不再二次压缩直接透传compression_handler.go 第 227-232 行测试见 compress_test.go 第 161 行TestShouldNotCompressWhenContentEncodingHeader。不可解析的Content-Type若响应Content-Type无法被解析为合法 MIME为对齐 gzip handler 行为将禁用压缩compression_handler.go 第 235-243 行。1xx 信息性响应100-199状态码会被原样转发而不进入压缩缓冲逻辑第 195-201 行。4.3 压缩开启后对响应头的影响一旦确认压缩中间件会添加Vary: Accept-Encoding响应头compression_handler.go 第 114 行确保中间缓存/CDN 能按编码区分缓存副本删除Content-Length因为压缩后长度未知并写入Content-Encoding: gzip | br | zstd第 280-283 行。五、空 Content-Type 的处理自动 MIME 嗅探若上游响应没有设置Content-Type或其为空compress中间件会按 MIME 嗅探标准自动探测内容类型并把探测到的 MIME 类型写回Content-Type响应头。这样设计是为了让后续的类型黑/白名单判断excludedContentTypes/includedContentTypes有一个可靠的依据同时也能改善客户端浏览器对无类型响应的处理。注意这是 gzip 压缩路径由底层gzhttp库提供的行为源码中以 TODO 形式标注该行为仍需在 Brotli/Zstd 路径对齐见 compression_handler.go 第 167 行的注释因此对缺失Content-Type的响应压缩判定依据的是嗅探后的结果。六、gRPC 与流式响应注意事项官方文档特别强调application/grpc永远不会被压缩。这一点有两层保障构造时application/grpc已被无条件写入排除列表excludescompress.go 第 51 行因此 gRPC 的响应Content-Type必然命中排除规则对请求侧若请求的Content-Type已是application/grpcgRPC-Web / gRPC 网关转发场景则直接在 compress.go 第 145-150 行提前放行中间件完全不介入。同样地text/event-streamSSE服务端推送也不适合压缩——源码在 compress.go 第 146 行的注释中明确指出对这类请求响应不应被压缩对应历史 issue traefik#2576。如需禁止压缩 SSE 或其它特殊媒体类型可在excludedContentTypes中显式补充例如http: middlewares: test-compress: compress: excludedContentTypes: - text/event-stream - application/grpc七、校验与验证结合测试理解行为边界仓库自带的测试对压缩中间件的每个关键行为点都做了断言是理解边界条件的绝佳教材。除了上文提到的TestNegotiation编码协商与TestShouldNotCompressHeadRequestHEAD 请求外还包括compress_test.go 第 186 行TestShouldNotCompressWhenNoAcceptEncodingHeader请求无Accept-Encoding时不压缩未配置defaultEncoding的场景第 207 / 229 行请求头为identity或空串时不压缩第 305 行TestShouldNotCompressWhenSpecificContentType与第 397 行TestShouldCompressWhenSpecificContentType分别验证黑名单与白名单行为第 592 行TestMinResponseBodyBytes验证最小字节阈值生效第 645 行Test1xxResponses验证 1xx 信息性响应的透传处理。若要在本地跑一遍该中间件的单元测试可在仓库根目录执行go test ./pkg/middlewares/compress/...八、小结compress中间件通过配置声明 内容协商 缓冲后决策三层机制实现透明响应压缩配置层encodings/defaultEncoding/ 类型黑白名单 / 最小字节数决定支持范围与降级策略协商层依据请求Accept-Encoding与 q 值从zstd、br、gzip中挑选编码执行层则在响应未达阈值前先缓冲、达标后即时切换到压缩写出并对已压缩、无类型、gRPC、SSE、HEAD/1xx 等场景做了稳妥的直通保护。理解这些判定顺序就能在真实网关场景中精准预测某类响应的最终形态避免对 WebSocket、事件流或二次压缩等问题做出错误假设。【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefik创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考