Nginx 开启 gzip 之后,传输量到底降了多少

2026-10-12 约 1200 字 Nginx

给一个静态站点做首屏优化时,先量了一遍传输量:单个 JS 产物 1,165,571 字节, 单个 CSS 373,953 字节。当时第一反应是去做代码分割,后来发现更简单的原因—— 这两个文件根本没有被压缩过。

怎么发现的

用 curl 带上 Accept-Encoding 请求头,看响应里有没有 Content-Encoding:

curl -sI -H 'Accept-Encoding: gzip, br' \
  https://example.com/assets/app.js | grep -iE 'http/|content-length|content-encoding'

返回的头里只有 Content-Length: 1165571,没有 Content-Encoding。 而同一站点的 HTML 首页却带着 Content-Encoding: gzip。 这个差异直接指出了问题所在。

为什么 gzip on 单独写没有用

打开 /etc/nginx/nginx.conf,能看到这样的片段:

gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_types text/plain text/css application/json
#            application/javascript text/xml application/xml application/xml+rss
#            text/javascript;

关键在最后一行:nginx 的 gzip_types 默认值只有 text/html。 所以 gzip on 生效的范围内,只有 HTML 会被压缩, JS、CSS、JSON 全部按原样传输。这正好解释了上面观察到的现象。

改法

既可以在 http 块里统一配置,也可以在单个 server 里配置。这次只对一个站点生效, 所以写在了 server 块中:

server {
    listen 80;
    server_name example.com;

    gzip on;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_min_length 512;
    gzip_types
        text/plain
        text/css
        text/xml
        application/javascript
        application/json
        application/xml
        image/svg+xml;

    # ...
}

改完先校验再热加载,避免把整个站搞挂:

nginx -t && systemctl reload nginx

实测结果

资源 压缩前 gzip 后 节省
app.js 1,165,571 B 385,784 B 67%
app.css 373,953 B 约 60 KB 84%

文本类资源的压缩比通常都在 60%–80% 之间,只改几行配置就能拿到, 性价比远高于为同样体积去做代码拆分。

注意事项 不要给已经压缩过的格式开 gzip:JPEG、PNG、MP4、WebP、WOFF2 本身已是压缩数据, 再压一遍只会浪费 CPU,体积基本不变,甚至略微变大。

几个容易漏掉的点

gzip_vary

加上 gzip_vary on 会输出 Vary: Accept-Encoding。 如果前面有 CDN 或缓存层,不加这个头可能出现「压缩版和未压缩版发错对象」的问题—— 支持压缩的客户端收到了未压缩内容,或者反过来。

gzip_proxied

通过 proxy_pass 反代到后端的接口,默认不会被压缩。 要让 nginx 对上游响应也做 gzip,需要显式打开:

gzip_proxied any;

但要注意,如果后端已经把响应压缩过一次(比如 Express 的 compression 中间件), 这里再开会导致双重压缩,需要二选一。

gzip_static

如果构建阶段能产出 .gz 文件,可以用 gzip_static always 直接发预压缩版本,省掉运行时的压缩开销。适合访问量大、内容不常变的静态资源。

验证是否真的生效

# 有 gzip 时
curl -sI -H 'Accept-Encoding: gzip' https://example.com/assets/app.js \
  | grep -i content-encoding
# 期望输出: Content-Encoding: gzip

# 不带 Accept-Encoding 时应无压缩
curl -sI https://example.com/assets/app.js | grep -i content-encoding
# 期望无输出

两条都符合预期,才算配置真正到位。只看配置文件容易被 include 顺序、 默认值覆盖这类问题坑住。

← 返回笔记列表 下一篇:frp 内网穿透要点 →