先讲一件运维群里隔三差五就会冒出来的事。有人给一台跑 API 的机器开了 gzip,级别拉到 9,上线第二天兴冲冲地去看出网流量图——确实跌了七成,效果立竿见影。但同一个页面上,CPU 使用率从原来的三成爬到了接近满载,load 翻倍,P99 响应时间从 40 毫秒涨到 300 多毫秒,业务方开始报超时。最后他做的是把压缩级别从 9 降回 4,出网流量只比 9 级多了几个百分点,CPU 直接掉回去一半。
这个反差里藏着一件很多人没算过账的事:压缩不产生价值,它只是把一种资源换成另一种资源。省下来的是出网字节,花掉的是 CPU 时间和首字节延迟。既然是交换,那它就一定存在"划算"和"亏本"两种情况,而不是"开了就好"。
所以本文要立的判断很明确——问题从来不是"要不要开压缩",而是"对什么内容开、开到几级、在哪一层开"。这笔交换划不划算,取决于两件事:内容本身的可压缩性,以及这条链路上真正稀缺的是带宽还是 CPU。判断标准也很直接:出网带宽是瓶颈的时候(海外节点、按流量计费、移动端用户),压缩稳赚;CPU 已经是瓶颈的时候(高 QPS 的 API、已经跑在七八成 CPU 的机器),再往上拉压缩级别就是自杀。
要把这笔账算清楚,得先把"换"的两端摆明白。压缩带来的变化体现在三个量上,两个是付出,一个是收益。
收益很好算,就是响应体压缩前后之差乘以请求数。不同内容类型的压缩比差异极大,下面是行业里常见的参考区间,仅供建立量级感,你自己的数据必须实测:
这里有个很容易被算漏的点:收益是"每次传输"都在省,而压缩的 CPU 成本在静态预压缩场景下只付一次。一个 200KB 的 JS 文件,预压一次花掉几十毫秒 CPU,之后几万次下载都不再花 CPU,净赚。而一个每次请求都要重新生成的动态响应,压缩成本是每一次请求都要付一遍的。这两件事的账本结构完全不同,所以处理方式也必然不同——后面讲静态预压与动态压缩那一节会展开。
CPU 这一侧的口径要稍微绕一点。常见的定性算法是:压缩一段数据花掉的 CPU 时间 ≈ 原始字节数 ÷ 该算法在该级别下的压缩吞吐速度。压缩吞吐速度通常用 MB/s 表示,随算法、级别、数据内容、CPU 型号变化很大,从几十 MB/s 到几百 MB/s 都有,这个数字只能实测,不要套用别人的跑分。
有了这个口径,就能把"每 GB 出网流量烧多少 CPU"估出来。举个假设性的算法演示(以下为典型部署思路的算账示例,并非特指某一真实客户,数值均为假设):某站点每天出网 500GB 原始文本,压缩后降到 150GB,省下 350GB 传输;压缩这 500GB 原始数据的 CPU 成本,按单核 100MB/s 的压缩吞吐粗估,大约需要单核跑 5000 秒,摊到一天 86400 秒里大约是单核 6% 的占用。看起来不多,很划算。
但如果这 500GB 是由 2000 QPS 的小响应堆出来的(平均每个响应只有几 KB),情况就变了:小响应上压缩的单位成本更高——每个响应都要付出算法初始化、字典准备、块头写入这些固定开销,而这些开销摊在几 KB 的数据上就非常显眼。加上高 QPS 本身就把 CPU 吃得七七八八,这时候压缩就是压垮骆驼的那根稻草。这也是为什么"大文件压缩很划算,小响应压缩要谨慎"会成为一条经验。
第三个量最容易被忽略:压缩会影响首字节时间。动态压缩不是"拿到完整响应再压一遍"这么简单,它要在响应生成过程中边生成边压,压缩器内部有缓冲区,数据要攒够一定量才会吐出一段输出。这带来两个后果:一是首字节要等第一个压缩块产出;二是如果响应体小于缓冲区阈值,可能根本压不出来任何东西,白等一场。
压缩和分块传输(chunked)配合时,服务端的行为、缓冲区刷新的时机,都会影响客户端拿到第一个字节的时间。对首屏敏感的页面来说,压缩省下的传输时间和它加上的编码时间,两者并不总是前者赢——尤其是文本只有几 KB、而客户端和服务器之间延迟很低(比如同机房、同地域)的情况下,省下的那点传输时间可能还不如压缩花掉的时间多。
还有一个连带影响在缓存层:压缩会改变响应体的字节长度,因此会影响 ETag、Content-Length,也会影响 CDN 和共享缓存的缓存键。这些地方处理不当,会出现缓存命中率下降或者缓存串味的问题,Vary 那一节会讲到。
既然是交换,判断标准就只有一个:这条链路上真正稀缺的是带宽还是 CPU。稀缺的那一项,压缩才有价值;已经富余的那一项,压了也是白压。
还有一种容易被误判的情况:带宽包年是固定额度且长期用不满。这时候压缩的收益只是"更不容易顶到峰值",并不产生实际的成本节省,而 CPU 开销是实打实的。这种情况下,压缩的级别就应该往低调,甚至对动态内容直接不开。
反过来,如果机器上 CPU 大量空闲、带宽却吃紧,那把压缩级别往上调一档就是白捡的钱。判断的动作其实很简单:看监控上 CPU 和出网带宽这两条曲线,谁先接近天花板,就为谁做优化。
三种算法不是"新的一定比旧的好"的关系,它们的性格差别很大,适合待的位置也不一样。挑错位置,比挑错级别更浪费。
gzip(底层是 DEFLATE)的最大优势不是压缩比,而是任何客户端都支持。从二十年前的浏览器到现在的嵌入式设备、爬虫、命令行工具,Accept-Encoding 里带 gzip 是常态。这决定了它的位置:兜底。当客户端不支持 brotli 时,退回 gzip;当 CDN 或者中间设备对 brotli 支持不确定时,用 gzip。
级别范围通常是 1 到 9。中等级别(4–6)的性价比最高:1 级太快但压缩比损失明显,9 级的 CPU 开销涨得厉害而体积收益很小。默认级别通常在 1 或 6 附近(不同实现默认值不同,需确认你自己用的版本),生产环境我一般建议显式写出来,别依赖默认值。
brotli 相对 gzip 的核心差异是内置了一个体积很大的静态字典,里面预置了大量 Web 领域常见的文本片段(标签名、常见 CSS 属性与关键字、常见 JS 惯用语等)。这意味着两件事:
brotli 的级别范围是 0 到 11,这是它最容易踩坑的地方。高等级(9–11)使用的是非常大的滑动窗口和极其昂贵的匹配搜索,CPU 和内存开销会急剧上升——11 级压缩单个大文件时占用几百 MB 内存是常见的量级描述(需实测确认)。这个开销对于"构建期跑一次"完全可接受,对于"每个请求跑一次"就是灾难。
所以 brotli 的位置很清晰:静态资源用最高等级预压缩,动态响应用 4–5 级。把 brotli 11 级用在动态内容上,是我在生产环境里见过的最典型的压缩误用之一。
zstd 的设计目标是在相近的压缩比下提供明显更快的压缩速度,以及非常快的解压速度。它的级别范围很宽(通常 1 到 19,更高还有带额外内存开销的超高档位),低档位速度快,高档位压缩比好,选择空间比 gzip 大得多。它还支持用业务样本训练专用字典,对结构固定的短报文(比如固定 schema 的 JSON、日志行)效果很好。
解压快这件事在什么地方值钱?在解压次数远多于压缩次数的场景:一份日志压一次,之后被查询和分析解压很多次;一份数据在对象存储里压一次,被读取很多次。这类场景里解压成本占比很高,zstd 的优势就会非常明显。
但 zstd 有个硬约束:浏览器生态对它的支持度远不如 gzip 和 brotli。客户端在 Accept-Encoding 里声明支持 zstd 的比例、以及各浏览器与各版本的支持情况都在变化,需要你自己去确认目标用户群的实际情况。因此 zstd 的现实位置主要是:
说白了就是一句:面向浏览器用 gzip 兜底 + brotli 提效,不面对浏览器的链路上 zstd 更划算。
算法是通过内容协商选出来的:客户端在请求头 Accept-Encoding 里声明自己支持哪些编码(比如 gzip, deflate, br),服务端挑一个返回,并在响应头里用 Content-Encoding 标明实际用了哪个。
坑在于缓存。同一个 URL 现在有了多种不同的字节表示——不压的版本、gzip 的版本、brotli 的版本。如果服务端没有在响应里带上 Vary: Accept-Encoding,中间的 CDN 和共享缓存就不知道这个响应是依赖于请求头的,它会拿一个 URL 对应的缓存条目去服务所有请求。
后果有两种,一种轻一种重:
所以 Vary: Accept-Encoding 必须设,而且要在所有可能被缓存的压缩响应上都设。同时要注意,带上了 Vary 之后缓存键会变多,同一个资源会在缓存里存多份,这是必须接受的成本。还有一个连带检查项:ETag。有些实现会在压缩响应上把 ETag 标记为弱校验或者改写后缀,以区分不同编码版本,具体行为取决于你用的服务端版本,需要实测确认,别想当然。
这一节是全文最应该记住的部分。前面算的所有账,都建立在"内容可压缩"这个前提上。而现实中很多站点的出网流量大头,恰恰是不可压缩的内容。
JPEG、PNG、WebP、AVIF、MP4/H.264/H.265、ZIP、GZ、7z、以及 PDF 内部已经压缩过的流,这些格式的共同点是:它们在自己的编码过程中已经做过熵编码,输出的字节分布已经接近随机。而任何通用压缩算法(DEFLATE、brotli、zstd)的收益来源都是"找到并消除统计冗余",面对已经接近随机的数据,它们找不到冗余,输出体积几乎不掉,但整个编码流程的 CPU 是一点没少花。
折算下来就是:体积收益 0%–2%(行业参考,需实测),CPU 成本 100%。这不是低效,这是纯亏。而且这类内容往往是大文件,压一个大文件花掉的 CPU 时间比压一百个小文本还多。
几个特别容易漏掉的:
压缩格式本身是有头的。gzip 有固定长度的文件头、deflate 块头、末尾还有校验和与长度字段,加起来是几十字节的固定开销;brotli、zstd 也各有自己的头部开销。同时还有算法初始化、字典准备这类固定成本。
当响应体只有几百字节时,这些固定开销很容易超过压缩省下来的字节。极端情况下压缩后的响应比原文还大。即便没有变大,赚到的那十几个字节也完全抵不过 CPU 启动成本。所以几乎所有服务端实现都提供了最小压缩阈值这个配置项(常见的默认值在几百字节到一千多字节之间,各实现不同),务必设上。
加密内容的输出在设计上就必须接近随机(否则会泄露明文信息),因此不可压缩。端到端加密的报文、密文落盘的数据,开压缩没有任何意义。
流式与实时音视频则要反过来考虑延迟:压缩器需要缓冲才能产出输出块,这个缓冲会直接叠加到端到端延迟上。对直播、实时通话这类场景,为了省一点带宽而引入几十到几百毫秒的缓冲,是完全不可接受的取舍。
| 内容类型 | 典型可压缩性(行业参考,需实测) | 值不值得压 | 推荐算法与级别 | 说明 |
|---|---|---|---|---|
| HTML / CSS / JS(已 minify) | 高,通常可压到原体积的两成上下 | 非常值得 | 静态:brotli 9–11 预压缩;动态:brotli 4–5 或 gzip 4–6 | 标签与关键字重复度高,大字典收益明显;静态走构建期预压缩,运行时零开销 |
| JSON / XML API 响应 | 中高,通常两成到三成半,视字段重复度而定 | 值得,但要先算 CPU | 动态 gzip 4–6;内部链路可 zstd 3–5 | 字段名重复多则收益好;塞满随机 ID 与一次性 token 时收益明显下降,高 QPS 场景需实测 |
| 图片(JPEG / PNG / WebP / AVIF) | 极低,接近 0%–2% | 不值得 | 不压缩,在配置里按类型排除 | 格式内部已做熵编码,再压体积不掉、CPU 照烧;这是被忽略最多的浪费源 |
| 视频(MP4 / H.264 / H.265 / 切片) | 基本为 0 | 不值得 | 不压缩;实时流尤其不能压 | 码流本身已压缩;对流式内容开压缩还会引入缓冲与额外延迟 |
| 字体 woff2 | 几乎为 0 | 不值得 | 不压缩(早期 woff 可压) | woff2 内部已用 brotli 压过,外层再套一层基本无效 |
| 已压缩归档(zip / gz / 7z)与 PDF 内已压缩流 | 接近 0,个别情况略微变大 | 不值得 | 不压缩 | 二次压缩没有冗余可消;下载站、软件分发类业务要重点排除这一类 |
| 小于 1KB 的小响应 | 不确定,存在变大的可能 | 通常不值得 | 设最小压缩阈值(常见 1KB 上下,需实测定档) | 头开销与算法初始化成本可能超过省下的字节;高 QPS 下这一点会被放大 |
| 含敏感信息且含攻击者可控输入的响应 | 不论压缩比,安全优先 | 不该压 | 对该路径单独关闭压缩 | BREACH 类压缩侧信道可通过响应长度推断敏感内容,属硬性红线 |
| 纯文本日志 / 内部 RPC 体 / 落盘数据 | 高 | 值得 | zstd 3–8,可用业务样本训练字典 | 不直接面向浏览器,解压极快是核心优势;落盘场景压一次解压多次,收益清晰 |
前面反复提到的"静态"和"动态",其实是两种完全不同的成本结构,处理方式必须分开。
静态资源(JS、CSS、HTML 模板产物、SVG、大型 JSON 配置)在构建阶段就能确定内容。那就在构建流程里直接生成压缩产物——比如同时产出 app.js、app.js.gz、app.js.br——服务端运行时直接把预压好的文件发出去,CPU 开销为零。主流 Web 服务器都支持这种行为(Nginx 的 gzip_static 与 brotli 静态模块这类机制),命中预压文件时就跳过运行时压缩。
这样做最大的好处是:既然 CPU 只在构建期花一次,那就可以毫无顾忌地用最高等级。brotli 11 级压一个大 JS 文件可能要几秒甚至更久、占几百 MB 内存,但这是一次性的、离线的、可以慢慢跑的成本,换来的却是这个资源每一次下载都能少传几个百分点的字节。这笔账在静态资源上永远划算。
代价也很清楚,有三条:
API 响应、SSR 页面、个性化内容,这些每次请求的内容都不一样,只能在运行时压。这时候纪律只有两条:级别压住,阈值设上。
级别为什么必须压住,下一节讲。阈值为什么必须设上,前面已经讲过——小响应的收益抵不过固定开销。两个配在一起,动态压缩的成本才是可控的。
还有一条容易忽略:动态压缩要按内容类型做排除。不要写一个"所有响应都压缩"的规则,而是要写成"对文本类响应压缩、对图片视频字体归档类排除"。很多默认配置的压缩类型列表里已经带了 text/html 一类,但有些实现会默认包含 * 或者把 image/* 也带上,这种默认值要自己核一遍。
这是本文第二个必须立的判断。压缩级别从 1 级往上走,体积收益是递减的,而 CPU 开销是递增的,有的算法还是超线性递增。
具体量级(行业参考,不同内容与实现差异极大,必须实测):
所以定档的建议是直接给的:动态内容 gzip 4–6 级、brotli 4–5 级、zstd 3–5 级;静态预压缩 brotli 9–11 级、gzip 9 级。把级别从 4 拉到 9,多花的那部分 CPU 换不回等价的流量——这才是"级别拉满"真正的代价。
要强调的是,这些档位是建立在"内容是可压缩文本"这个前提上的。如果内容本身压不动,级别调到几都是白搭,先把内容类型筛对,再谈级别。
判断压缩是不是已经变成瓶颈,不需要什么特殊工具,看几个现有指标的对应关系就够了。
给一个可以直接落地的定性口径,所有数字都要用你自己的环境实测:
行业参考的量级感是:中等级别压缩的吞吐通常在单核几十 MB/s 到一两百 MB/s 之间(需实测确认,不同算法、级别、数据内容、CPU 型号差异巨大),折算下来每 GB 原始数据的压缩时间在单核几秒到十几秒这个量级。这个数看着不大,但乘以每天几百 GB 的出网量、再叠加高 QPS 下的小响应固定开销,就完全可能吃掉一整台机器的余量。
上面的现象只是嫌疑,确认还要再走一步。最省事的办法是采样:在压测或者高峰时段抓一份 CPU 采样的火焰图或 top 函数排名,看排在前面的函数里有没有压缩库的编码函数(zlib 的 deflate 系列、brotli 的编码入口、zstd 的压缩入口)。有,就坐实了。没有,就说明 CPU 是别的地方吃的,别调优错了方向。
还有一条:看压缩吃的是哪个核。Web 服务器通常是多 worker 进程模型,压缩发生在处理请求的那个 worker 所在的核上。核数少、worker 少的时候,压缩会把某几个核打满而其他核闲着,看起来整机 CPU 不高但实际已经在排队。这类分布问题跟软中断集中在单核上是同一个道理,判断时要按核看,不要只看整机平均值。
压缩会泄露信息,这不是理论问题。BREACH、CRIME 这一类攻击利用的都是同一件事:压缩后的长度依赖于内容,而内容中的重复会缩短长度。
推理过程是这样的。假设一个响应里既包含用户的 CSRF token(秘密),又包含攻击者能控制的输入(比如 URL 参数回显到页面上)。攻击者在自己的输入里放一段猜测字符串,如果这段猜测恰好和秘密的某个部分相同,那么响应里就出现了重复内容,压缩器会把它消掉,响应长度就会变短。攻击者观察响应长度,逐字节试,就能把秘密一个字节一个字节地试出来。
它成立需要几个条件同时满足:响应里包含攻击者想拿到的秘密;响应里同时包含攻击者可控的输入;秘密在响应中重复出现(否则压缩无从谈起);攻击者能反复发起请求并观测到压缩后的响应长度。这四个条件凑齐的场景比你想象的要多——带 token 的表单页、带回显的搜索结果页、把用户标识渲染进 HTML 的页面,都在这个范围内。
所以结论是硬性的:对同时包含敏感信息与攻击者可控输入的响应,不要开压缩。如果业务上必须压,就要做针对性缓解,比如让 token 每次请求都随机化(这样秘密不再以固定形式重复出现)、或者把秘密移出会被回显的上下文、或者对这类路径单独关闭压缩并配合其他防护手段。至于给响应加随机填充来掩盖长度这种做法,会破坏长度语义、影响面广,是否采用要具体评估,别当成默认方案。
顺带说一句:TLS 层压缩(CRIME 的直接目标)在现代实现中基本已经默认关闭,但 HTTP 层的响应压缩是另一回事,它依然可能成为侧信道,两者不要混为一谈。
压缩可以开在三个位置,各有各的适用面:
原则是:一条链路上只压一次。重复压缩的表现是 CPU 花两遍、延迟叠两次,而收益为零——因为第二个压缩器面对的是已经被压过的数据,压不动了。
最常见的重复场景是 CDN 加源站:CDN 已经压过了,源站就不需要再压。回源请求可以让 CDN 用 identity 回源(源站只出不压的版本,由边缘统一压),或者明确约定由谁负责。反过来,如果源站压了而 CDN 也开了压缩,CDN 通常不会因为已经压缩过就重复压,但配置不清晰时就容易出现两边都压或者两边都不压的情况,需要实测确认。
同样的道理适用于反向代理加应用框架:如果 Nginx 已经开了 gzip,应用里的压缩中间件就该关掉,或者反过来。选一层的标准是"哪一层更容易拿到业务语义、哪一层更容易做缓存",通常选反向代理层,敏感路径的例外单独在业务层关闭。
把前面的判断落到配置上,就是这八项。每一项都要有人确认过,不能靠默认值。
Vary: Accept-Encoding。用 curl 带不同 Accept-Encoding 各请求一次,看响应头和缓存行为是否符合预期。改完之后做一轮验证:用同样的流量模型压一次,记录开关前后的 CPU、出网带宽、TTFB 与 P99 四个数。只有这四个数的变化都符合预期,这次调整才算做完。只看出网流量降了就收工,是开头那个故事重演的开始。
整篇文章里出现的所有压缩比、吞吐、耗时倍数,都是"行业参考 / 通常会出现"的口径,不是任何实测数据,也不构成任何性能承诺。原因不是谦虚,是这些数字的离散度实在太大:
所以正确的做法是:拿你自己的真实响应样本,在你的目标机型上,用你准备上线的级别,跑一遍压缩吞吐与压缩比,再拿这两个数去算账。样本要覆盖你业务里的主要响应类型,挑几个有代表性的路径分别测,别用一个平均值代表全部。
承载这些 Web 与 API 的服务器环境本身也在这个账里。压缩吃的是 CPU 核,核数与主频直接决定你能不能扛住压;出网带宽的计费方式(固定带宽还是按流量)决定压缩的收益能不能换成钱。这两件事在选机器形态时就要想清楚——需要自己决定核数与压缩策略、不想受虚拟化层干扰的,我一般会建议直接看裸金属形态;弹性需求强、流量有波动的,云主机形态更灵活。一万网络深耕 IDC 19 年(成立于 2007 年),这类承载 Web 与 API 的服务器环境从一万云 ¥25 元/月起,到华南 ¥799 元/月起、华东 ¥699 元/月起、华北 ¥899 元/月起的大陆节点,再到裸金属 E5-2620 / 32G / 1T ¥999 元/月起、双路 E5-2698v4×2 / 32G / 1T ¥3999 元/月的档位都有覆盖(以上起价档通常对应最低配置与特定付款方式,实际成交价以下单时核算为准,以官网实时价为准)。核数够不够扛住你要开的压缩级别,是选型时可以直接拿来问的一个具体问题。
有可能,取决于内容大小和客户端链路。压缩省的是传输时间,花的是编码时间加首块缓冲时间。当响应体很小(几 KB)而客户端与服务器之间延迟很低(同地域、同机房)时,省下的传输时间可能是几毫秒,而编码加缓冲的时间与之相当甚至更多,净结果就是变慢。反过来,跨地域链路、移动端弱网、大体积文本,压缩省下的传输时间远大于编码时间,就是明显变快。判断方法很直接:同一条路径开关压缩各测一次,比较 TTFB 与完整下载完成时间的 P50 和 P99。首屏的关键资源记得走静态预压缩,运行时零开销,就不存在这个权衡了。
通常不需要,而且不应该。CDN 边缘压一次之后,缓存命中的请求都不再产生压缩 CPU,这是最省的结构;源站再压一遍,CPU 花两遍而收益为零,因为第二遍面对的数据已经压不动了。合理的分工是:源站只出不压的版本(回源用 identity),由边缘统一压;或者明确约定由源站压、边缘不重复处理。关键是要实测确认——直连源站和过 CDN 各用 curl 测一次,看 Content-Encoding 出现几次、响应体字节数是多少,别靠配置文件的字面意思下结论。就算 CDN 压了,源站的静态预压缩产物仍然有价值:回源时把预压好的版本给 CDN,能省回源带宽。
不能,这是明确的。11 级用的是极大的滑动窗口和极其昂贵的匹配搜索,单文件的内存占用可以到几百 MB 量级,压缩耗时相对中等等级可能涨到十倍甚至几十倍(行业参考,需实测)。这个成本放在构建期跑一次完全没问题,放在每个请求跑一次就是灾难——请求一多,内存和 CPU 同时告急,响应时间直接崩。动态响应用 brotli 就老老实实用 4–5 级,想要更高压缩比就走静态预压缩这条路,别在运行时硬扛。顺带提醒:有些实现的 brotli 默认级别就偏高,上线前务必把级别显式写出来。
先按可能性从高到低排查。第一,你的出网流量大头本来就是不可压缩的内容——图片、视频、字体、下载包。很多站点这两类内容占出网流量的八成以上,把文本全压掉对总量影响也有限,这种情况下先去优化图片格式和视频码率,收益比调压缩大得多。第二,压缩没真正生效:Content-Encoding 响应头有没有出现?用 curl -H 'Accept-Encoding: gzip' -I 看一眼,没有就是没生效。第三,内容本身接近随机:响应体里塞满随机 ID、token、Base64 片段,压不动。第四,小响应占比太高,被最小阈值挡掉了大部分请求。这四个查完,原因基本就出来了。
表现是缓存串味,而且症状很迷惑。轻的一种:支持 brotli 的客户端拿到了 gzip 版本的缓存,功能正常,只是白白浪费了压缩比,看不出来。重的一种:不支持 brotli 的客户端拿到了 brotli 版本的缓存,解不开,直接乱码或报错。症状是同一个 URL 在某些客户端正常、某些客户端炸,而且跟浏览器版本强相关。排查时如果你只盯应用层,会怀疑很久——因为应用层的逻辑完全没问题,问题出在中间缓存把"依赖于请求头的不同响应"当成了同一个响应。所以规则是死的:只要响应内容随 Accept-Encoding 变化,就必须带 Vary: Accept-Encoding。带上了之后缓存条目会变多,这是必须接受的代价。
判定条件是四个同时成立:响应里含攻击者想拿的秘密(token、用户标识、会话信息一类);响应里同时含攻击者可控输入(URL 参数回显、搜索词回显、用户名一类的反射内容);这个秘密在响应中重复出现;攻击者能反复请求并观测到压缩后的长度。四个都满足,就必须关——或者用每次请求随机化 token 的方式让秘密不再以固定形式重复。不要觉得"我的 token 藏在很深的地方就没事",压缩器看到的是字节流,它不管语义。落地方式是按路径列清单单独处理,而不是全局关压缩——全局关了,你为了一小部分路径牺牲了整站的带宽账。
三个办法,从粗到细。最粗的是开关对比:同样的流量模型,开与关各跑一段,直接比 CPU 使用率的差值,再除以这段时间的出网字节数,就得到"每 GB 出网流量的 CPU 成本"这个可以直接拿来做决策的数。中等精度的是看耗时拆分:在反向代理层比较请求总耗时与上游耗时的差值,压缩就发生在这一段里,差值随级别变化的情况就是压缩的成本曲线。最细的是采样:抓一份 CPU 采样数据,看压缩库的编码函数在排名里占多少百分比,同时按核看分布,确认压缩是不是把某几个核打满了。三个办法里第一个最实用,因为它直接给出能拿去和带宽单价对比的量。
要设,这是几乎必开的一项。理由是压缩格式有固定头部开销(gzip 的头、块头、尾部校验与长度字段加起来几十字节),还有算法初始化与字典准备的固定成本,这些成本摊在几百字节的数据上非常显眼,极端情况下压缩后反而更大。行业常见的默认值在一千字节上下(不同实现默认值不同),可以以此为起点:小响应占比高、QPS 高的业务往上调;大响应为主的静态资源站可以维持默认或下调。具体档位要拿你自己的响应体大小分布来定——先把响应大小的 P50、P90 统计出来,再决定阈值卡在哪,比拍脑袋强得多。
文中出现的所有压缩比区间、压缩吞吐与耗时倍数的量级描述,均为行业公开经验的参考口径,不构成任何实测数据、跑分结果或性能承诺。三种算法(gzip/DEFLATE、brotli、zstd)的级别范围、典型性格与适用位置,属于公开技术文档的通用描述,具体行为随压缩库版本、服务端模块版本与实际配置而异;BREACH/CRIME 一类压缩侧信道的描述为公开的攻击原理性介绍,实际风险取决于你自己的响应结构与防护配置,请以实际安全评估结论为准。所有数值请以你自己的真实响应样本在目标机型上实测的结果为准。
三种算法的公开资料来自各自官方文档与 IETF RFC 相关条目,压缩比与吞吐区间属行业公开经验值,不指向任何一次具体测量。服务器与云主机的档位描述(一万云 ¥25 元/月起、裸金属 E5-2620 / 32G / 1T ¥999 元/月起等)来自一万网络官网 https://www.idc10000.net/ 的产品页面,未做任何外推。具体以签约时最新报价与合同为准,起价档通常对应最低配置与特定付款方式,实际成交价请以下单时核算结果为准。文中出现的部署思路与算账示例均为典型场景假设,并非特指某一真实客户或真实案例。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品