强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 11 章:内容压缩

第 11 章:内容压缩

内容压缩是提升 Web 性能最简单有效的方法之一。合理配置压缩可以减少 60-90% 的传输数据量。


11.1 压缩概述

为什么需要压缩

指标未压缩gzip 压缩提升
HTML 页面100 KB25 KB75%
CSS 文件80 KB18 KB77%
JSON API50 KB12 KB76%
JS 文件200 KB45 KB77%

压缩流程

客户端                                服务器
  │                                    │
  │── GET /data ─────────────────────→│
  │   Accept-Encoding: gzip, br       │
  │                                    │── 压缩响应
  │←── 200 OK ────────────────────── │
  │   Content-Encoding: gzip          │
  │   Content-Length: 12000            │
  │                                    │
  │── 解压响应 ──                      │

11.2 Accept-Encoding 与 Content-Encoding

请求头:Accept-Encoding

Accept-Encoding: gzip, deflate, br
Accept-Encoding: gzip, deflate, br, zstd;q=1.0

响应头:Content-Encoding

Content-Encoding: gzip
Content-Encoding: br
Content-Encoding: zstd

11.3 压缩算法对比

特性gzipdeflateBrotli (br)Zstandard (zstd)
压缩率
压缩速度
解压速度
浏览器支持所有所有现代浏览器Chrome 123+
典型压缩率70-80%70-80%75-85%75-85%
适用场景通用通用静态资源API 响应

11.4 gzip 配置

Nginx 配置

# 开启 gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;           # 压缩级别 1-9
gzip_min_length 256;          # 最小压缩字节数
gzip_buffers 16 8k;

# 压缩的文件类型
gzip_types
    text/plain
    text/css
    text/javascript
    text/xml
    application/json
    application/javascript
    application/xml
    application/xml+rss
    application/vnd.ms-fontobject
    application/x-font-ttf
    font/opentype
    image/svg+xml;

Node.js 配置

const express = require('express');
const compression = require('compression');
const app = express();

// gzip 中间件
app.use(compression({
    level: 6,                    // 压缩级别 1-9
    threshold: 1024,             // 最小字节数
    filter: (req, res) => {
        // 不压缩已压缩的内容
        if (req.headers['x-no-compression']) {
            return false;
        }
        // 默认过滤
        return compression.filter(req, res);
    }
}));

11.5 Brotli 配置

Nginx 配置

# 开启 Brotli(需要 ngx_brotli 模块)
brotli on;
brotli_comp_level 6;
brotli_types
    text/plain
    text/css
    text/javascript
    application/json
    application/javascript
    application/xml
    image/svg+xml;

优先级配置

# Brotli 优先,gzip 作为降级方案
gzip on;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript;

brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/json application/javascript;

11.6 Zstandard (zstd)

优势

  • 压缩速度比 gzip 快 3-5 倍
  • 压缩率接近 Brotli
  • 解压速度极快

使用示例

import zstandard as zstd

# 压缩
compressor = zstd.ZstdCompressor(level=3)
data = b"Hello, World! " * 1000
compressed = compressor.compress(data)
print(f"原始: {len(data)} 字节, 压缩后: {len(compressed)} 字节")

# 解压
decompressor = zstd.ZstdDecompressor()
decompressed = decompressor.decompress(compressed)
# 客户端请求
GET /api/data HTTP/1.1
Accept-Encoding: zstd, gzip, br

# 服务器响应
HTTP/1.1 200 OK
Content-Encoding: zstd
Content-Length: 1234

11.7 Transfer-Encoding

Transfer-Encoding 用于分块传输压缩,与 Content-Encoding 不同。

特性Content-EncodingTransfer-Encoding
压缩对象完整资源传输过程
代理处理通常原样转发逐跳处理
典型使用静态资源流式响应
# Transfer-Encoding: chunked + gzip
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Encoding: gzip

1a\r\n
<compressed chunk data>\r\n
0\r\n
\r\n

11.8 压缩策略最佳实践

静态资源

# 静态资源:高压缩率
brotli on;
brotli_comp_level 11;  # 最高压缩率(预压缩)
gzip on;
gzip_comp_level 9;

# 使用预压缩文件
location ~* \.(js|css|svg)$ {
    brotli_static on;    # 优先使用 .br 文件
    gzip_static on;      # 降级使用 .gz 文件
}

API 响应

# API:中等压缩率(平衡 CPU 和压缩率)
gzip on;
gzip_comp_level 5;
gzip_types application/json;

不需要压缩的情况

情况原因
已压缩文件(.jpg, .png, .zip)无法进一步压缩
小文件(< 256 字节)压缩收益不大
实时流压缩增加延迟

11.9 业务场景:CDN 压缩配置

# 完整的 CDN 边缘节点压缩配置
server {
    listen 443 ssl;
    
    # Brotli(首选)
    brotli on;
    brotli_comp_level 5;
    brotli_static on;
    brotli_types
        text/plain
        text/css
        text/javascript
        application/json
        application/javascript
        application/xml
        image/svg+xml
        font/woff2;
    
    # gzip(降级方案)
    gzip on;
    gzip_comp_level 5;
    gzip_vary on;
    gzip_proxied any;
    gzip_static on;
    gzip_min_length 256;
    gzip_types
        text/plain
        text/css
        text/javascript
        application/json
        application/javascript
        application/xml
        image/svg+xml
        font/woff2;
    
    # 静态资源(使用预压缩文件)
    location /static/ {
        brotli_static on;
        gzip_static on;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
}

⚠️ 注意事项

  1. 不要压缩已压缩内容:图片、视频、ZIP 等已压缩格式无需再压缩
  2. 压缩消耗 CPU:高并发场景需要平衡压缩级别和 CPU 使用
  3. 使用 gzip_static:预先压缩文件,避免运行时压缩开销
  4. Vary 头:压缩响应需要 Vary: Accept-Encoding
  5. 安全问题:BREACH 攻击针对压缩+加密的响应,敏感数据考虑不压缩

🔗 扩展阅读


下一章第 12 章:分块传输与流式响应 — Chunked Transfer、Trailer、SSE