网站服务器如何开启Gzip或Brotli压缩?配置方法与性能影响

页面加载慢,一个常被忽略的原因是"传的东西太多"。HTML、CSS、JavaScript 这些文本文件压缩后体积往往能小到原来的两三成,如果服务器不压缩就直接原样发送,等于浪费了带宽、拖慢了加载。开启压缩是最低成本的性能优化之一——不用改代码、不用换硬件,改几行配置就能让传输量大幅下降。

这篇文章讲清楚压缩的原理、Nginx 如何开启 Gzip、Brotli 模块怎么装和配、哪些内容类型压缩有效哪些无效、压缩带来的 CPU 开销怎么权衡,以及 CDN 侧压缩怎么配合。读完你能给站点配出一套合理的压缩方案,并用工具验证效果。

一、压缩为什么有用

HTTP 压缩的原理是:服务器在发送响应前,先用 gzip 或 brotli 这类算法对文本内容做压缩,响应头里带上 Content-Encoding: gzip(或 br),浏览器收到后自动解压还原。全程对用户无感,但传输的字节数大幅减少。

文本类内容压缩收益最高,因为它们的重复模式多,压缩算法正是靠"找重复"来降体积。经验值:

内容类型 压缩效果(经验值) 说明
HTML 压缩到 20%-40% 重复标签多,收益大
CSS 压缩到 15%-30% 收益大
JavaScript 压缩到 20%-40% 收益大
JSON / XML 压缩到 10%-30% 收益很大
纯文本 压缩到 30%-50% 收益明显
图片(jpg/png/webp) 基本无收益甚至变大 已压缩,别再压缩
视频/音频 无收益 已压缩
字体(woff2) 无收益 已压缩
PDF/压缩包 无收益 已压缩

所以压缩的核心原则是:只压缩文本类,不碰已压缩的二进制。给图片开 gzip 不仅没收益,还白白消耗 CPU,甚至因为压缩头开销让体积变大。

二、Nginx 开启 Gzip

Gzip 是 Nginx 内置模块(ngx_http_gzip_module),无需额外安装,开箱即用。默认是关闭的,需要在 http、server 或 location 段配置。

# 在 http 段配置(全局生效)
http {
    gzip on;                              # 总开关
    gzip_comp_level 5;                    # 压缩级别 1-9,越大越省带宽越耗 CPU,5 是常用平衡点
    gzip_min_length 1024;                 # 小于 1KB 的不压缩,太小的压缩收益不抵开销
    gzip_types text/plain text/css text/xml
               application/json application/javascript
               application/xml application/rss+xml
               image/svg+xml;             # 需要压缩的 MIME 类型
    gzip_vary on;                         # 根据 Accept-Encoding 返回 Vary 头,避免缓存错乱
    gzip_proxied any;                     # 对代理请求也压缩
    gzip_disable "msie6";                 # 老 IE6 不支持,禁用(现代站点基本可省)
    gzip_buffers 16 8k;                   # 压缩缓冲区
}

几个参数要特别注意:

  • gzip_types 必须显式列出。Nginx 默认只压缩 text/html(而且这个类型不需要写进 gzip_types 就会压缩),其他类型如 CSS、JS、JSON 都需要手动加上。这是最常见的"开了压缩但没效果"的原因。
  • gzip_comp_level 不要盲目拉满。级别从 1 到 9,压缩率提升越来越小、CPU 开销越来越大。实测 4-6 级是性价比区间,9 级相比 5 级往往只多压几个百分点,却显著更耗 CPU。静态资源更推荐预压缩(下方说明)。
  • gzip_min_length 防止小文件做无用功。小于这个值的响应压缩后可能更大,还费 CPU,设成 1024 字节较常见。
  • gzip_vary on 很重要。它让响应带 Vary: Accept-Encoding,防止 CDN 或代理把压缩版发给不支持压缩的客户端。
  • gzip_static(可选):让 Nginx 优先发送预先压缩好的 .gz 文件,省去每次实时压缩的 CPU。适合不常变动的静态资源。
# 需要模块 ngx_http_gzip_static_module(多数发行版包自带)
location ~* \.(css|js|svg)$ {
    gzip_static on;    # 存在同名 .gz 文件时直接发送,否则回退到实时压缩
}

预压缩文件的生成很简单,在发布流程里对静态资源跑一遍压缩命令即可,部署时把生成的 .gz 和 .br 一起上传到站点目录:

# 生成 gzip 预压缩文件(-k 保留原文件,-9 最高压缩比,离线压缩不占运行时 CPU)
gzip -k -9 style.css
# 生成 brotli 预压缩文件(需安装 brotli 命令行工具)
# RHEL 系可尝试 dnf install brotli;Debian/Ubuntu 用 apt install brotli
brotli -k -q 11 style.css

这样运行时的 Nginx 只需读文件并发送,压缩成本在构建阶段一次性付出,比每次请求实时压缩省得多。改动配置前建议先备份,升级 Nginx 时也要留意 gzip_static/brotli_static 模块是否仍可用。

改完先测试语法再重载:

sudo nginx -t && sudo systemctl reload nginx

三、Nginx 开启 Brotli

Brotli(br)是 Google 推出的压缩算法,对文本的压缩率通常优于 gzip,尤其是中等大小以上的文件(大致比 gzip 再小 15%-25%)。代价是压缩时 CPU 开销更高。Brotli 不是 Nginx 内置模块,需要编译安装或装第三方包。

安装方式取决于发行版和是否用官方包:

# 方式一:部分发行版仓库/第三方源提供预编译模块包(版本和 Nginx 需匹配)
# RHEL/CentOS/Rocky 系常在 EPEL 或 Nginx 官方源中找到
# Debian/Ubuntu 可尝试 apt 源中的 libnginx-mod-brotli(视版本而定)
# 安装前务必确认模块 ABI 与当前 Nginx 版本兼容

# 方式二:源码编译时加入模块(需先从源码拉取 ngx_brotli 并放到指定目录)
# ./configure --add-module=/path/to/ngx_brotli --with-compat
# 注意:用源码编译会替换发行版包,后续升级维护要一并处理,属于有风险操作

# 装好后确认模块已加载
nginx -V 2>&1 | tr ' ' '\n' | grep -i brotli

配置(Brotli 的指令前缀是 brotli):

http {
    brotli on;                  # 总开关
    brotli_comp_level 5;        # 1-11,越大越省带宽越耗 CPU,静态资源可设高些
    brotli_min_length 1024;
    brotli_types text/plain text/css application/json
                 application/javascript application/xml
                 image/svg+xml;

    # 也可用 brotli_static 发送预压缩的 .br 文件
    # brotli_static on;
}

Brotli 和 Gzip 可以同时开启:支持 Brotli 的浏览器(现代主流浏览器基本都支持)拿到 br,老客户端回退到 gzip。Nginx 会按 brotli 优先于 gzip 的顺序协商。

# 同时开启示例
gzip on;      gzip_comp_level 5;  gzip_types ... ;
brotli on;    brotli_comp_level 5; brotli_types ... ;

风险提示:编译第三方模块有版本兼容和升级维护风险,生产环境建议用发行版或官方源提供的模块包,并先在测试环境验证。升级 Nginx 时要注意模块需要重新匹配。

四、压缩的 CPU 开销与权衡

压缩不是免费的,它把"带宽成本"换成了"CPU 成本"。这里有几个权衡点:

维度 Gzip Brotli
压缩率 良好 更好(中小文件尤其明显)
压缩 CPU 开销 较低 较高(级别越高越明显)
浏览器支持 极广 现代浏览器普遍支持
是否需额外模块 不需要(内置) 需要额外模块
适用 通用默认 追求更小体积的场景

优化经验:

  • 静态资源用预压缩。CSS、JS、字体这类文件构建时就生成 .gz 和 .br,Nginx 用 gzip_static/brotli_static 直接发,运行时零压缩开销。这是最省 CPU 的做法。
  • 动态内容用中等级别。动态 HTML、API 响应实时压缩,级别设 4-5 即可,别用 9。
  • 根据 CPU 余量决定是否上 Brotli。CPU 紧张的机器优先保证 Gzip,CPU 充裕再开 Brotli 换更小的传输量。
  • gzip_min_length 兜底。小响应本就只有几百字节,压缩意义不大还费 CPU。

一个常见误判:有人担心"压缩会拖慢服务器",但在现代 CPU 上,对文本内容用中等级别实时压缩,单次开销通常在毫秒级,相比省下的带宽和加载时间,收益远大于成本。真正需要警惕的是对已压缩的图片视频再压缩,那是纯浪费。

五、验证效果

配置完别只靠感觉,用工具量一下。

# 看响应是否带 Content-Encoding
curl -I -H 'Accept-Encoding: gzip' https://example.com/
# 期望:Content-Encoding: gzip

# 看 Brotli 是否生效(支持 br 的客户端)
curl -I -H 'Accept-Encoding: br' https://example.com/
# 期望:Content-Encoding: br

# 对比压缩前后的传输大小
# 不压缩(发送 Accept-Encoding: identity)
curl -s -o /dev/null -w "未压缩大小: %{size_download} 字节\n" -H 'Accept-Encoding: identity' https://example.com/

# 压缩后
curl -s -o /dev/null -w "压缩后大小: %{size_download} 字节\n" -H 'Accept-Encoding: gzip' https://example.com/

# 查看实际使用的编码
curl -s -o /dev/null -w "%{content_type}\n" -H 'Accept-Encoding: gzip' https://example.com/

浏览器开发者工具的 Network 面板里,点开请求看 Response Headers 的 Content-Encoding 和 Content-Length,Size 列会显示"传输大小/资源大小",能直观看出压缩比。很多站点的 HTML/CSS/JS 压缩后能省 60%-80% 的传输字节。

六、CDN 侧压缩怎么配合

如果用了 CDN,压缩可以在"源站"和"CDN"两处发生,要理清关系,避免重复压缩。

  • 源站开启压缩,回源时省带宽;但 CDN 到用户的这一段是否压缩,取决于 CDN 的配置。
  • CDN 开启压缩,边缘节点对用户压缩,源站可以只发原始内容,让 CDN 统一处理。
  • 常见推荐:源站开 Brotli(回源传输小),CDN 侧也开压缩(用户传输小),并把 Vary: Accept-Encoding 处理好,确保缓存不串。

要注意 CDN 缓存的"压缩版本"问题:如果 CDN 把某资源的压缩版缓存下来,又发给了不支持的客户端,就会出错。Vary: Accept-Encoding 和 CDN 的缓存规则要一致,避免这类问题。

常见问题

Q:开了 gzip 但 CSS/JS 没压缩,为什么?
多半是 gzip_types 里没列对应 MIME 类型。Nginx 默认只压 text/html,CSS、JS、JSON、XML 等必须手动加进 gzip_types。

Q:压缩级别设 9 一定比 5 好吗?
不一定。9 级 CPU 开销明显更高,压缩率相比 5 级提升有限(常只有几个百分点)。静态资源建议预压缩,动态内容用 4-6 级。

Q:图片要不要开压缩?
不要。jpg/png/webp 本身就是压缩格式,再 gzip 基本没收益甚至变大,还浪费 CPU。图片优化应该另做(转 WebP、缩放尺寸、压缩质量)。

Q:Brotli 和 Gzip 能同时开吗?
能。现代浏览器优先拿 Brotli(br),老客户端回退 gzip,Nginx 会自动协商。CPU 紧张的机器可以只用 gzip。

Q:怎么确认压缩真的生效了?
用 curl -I -H 'Accept-Encoding: gzip' 看有没有 Content-Encoding: gzip,再用 curl -w '%{size_download}' 对比压缩前后的字节数,或在浏览器开发者工具看 Size 列。

总结

HTTP 压缩把文本内容压小后再传输,是零代码、低成本的性能优化,HTML/CSS/JS/JSON 收益最大,图片视频等已压缩格式不要压。

Nginx 内置 Gzip,核心是 gzip on、gzip_types 列全文本类型、gzip_comp_level 取 4-6、gzip_min_length 兜底、gzip_vary on 防缓存串;gzip_static 可发预压缩文件省 CPU。

Brotli 压缩率更好但需额外模块,级别 1-11,可与 gzip 并存;第三方模块编译有兼容和升级风险,建议用发行版包并在测试环境验证。

压缩是把带宽换成 CPU:静态资源优先预压缩,动态内容用中等级别,CPU 紧张先保 Gzip 再考虑 Brotli。

配置后用 curl -I -H 'Accept-Encoding' 和 size_download 对比验证;用 CDN 时源站和边缘的压缩策略要理清,Vary: Accept-Encoding 要处理好以免缓存串版。