27 / 36
Как работает сжатие ответов (gzip, Brotli)?
Полный ответ
Процесс согласования сжатия:
JavaScript
Браузер Сервер
| |
| -- GET /app.js |
| Accept-Encoding: gzip, deflate, br --> |
| |
| [Сервер выбирает лучший алгоритм] |
| |
| <-- 200 OK |
| Content-Encoding: br |
| Content-Type: application/javascript |
| [сжатое тело ответа] |
| |
| [Браузер распаковывает автоматически] |
Настройка на nginx:
Nginx
# nginx.conf
# gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # 1-9, баланс скорости и степени сжатия
gzip_min_length 256; # не сжимать мелкие ответы
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml
font/woff2;
# Brotli (нужен модуль ngx_brotli)
brotli on;
brotli_comp_level 6; # 1-11, для on-the-fly обычно 4-6
brotli_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml;
Предварительное сжатие (статические файлы):
Bash
# Сжатие при сборке -- максимальный уровень, не тратит CPU на лету
# gzip
gzip -k -9 dist/app.js # создаёт app.js.gz рядом с оригиналом
# Brotli (уровень 11 -- максимальный, медленный, но лучшее сжатие)
brotli -k -q 11 dist/app.js # создаёт app.js.br
Nginx
# nginx раздаёт предсжатые файлы
gzip_static on; # ищет .gz рядом с файлом
brotli_static on; # ищет .br рядом с файлом
Настройка в Express:
JavaScript
import express from 'express';
import compression from 'compression';
const app = express();
// Базовое сжатие (gzip по умолчанию)
app.use(compression({
level: 6,
threshold: 1024, // не сжимать ответы меньше 1 KB
filter: (req, res) => {
// Не сжимать уже сжатые форматы
const contentType = res.getHeader('Content-Type') || '';
if (/image| video |audio/.test(contentType)) return false;
return compression.filter(req, res);
},
}));
// Для Brotli в Express нужен shrink-ray-current или iltorb
import shrinkRay from 'shrink-ray-current';
app.use(shrinkRay({
brotli: { quality: 4 }, // на лету -- ниже уровень для скорости
}));
Сравнение алгоритмов:
JavaScript
Файл: React bundle (500 KB)
-----------------------------------------
Без сжатия: 500 KB (100%)
gzip -6: 145 KB (29%) быстро
gzip -9: 140 KB (28%) чуть медленнее
Brotli -4: 130 KB (26%) сравнимо с gzip по скорости
Brotli -11: 115 KB (23%) медленно, только для статики
📌 Реальные кейсы
Проверка сжатия в коде:
JavaScript
// Проверяем, сжимается ли ресурс
const resources = performance.getEntriesByType('resource');
for (const res of resources) {
if (res.decodedBodySize > 0 && res.transferSize > 0) {
const ratio = 1 - res.encodedBodySize / res.decodedBodySize;
const isCompressed = ratio > 0.1; // сжатие больше 10%
if (!isCompressed && res.decodedBodySize > 10_000) {
console.warn(
`Несжатый ресурс (${(res.decodedBodySize / 1024).toFixed(0)} KB): ${res.name}`
);
}
}
}
Webpack-плагин для предсжатия:
JavaScript
// webpack.config.js
import CompressionPlugin from 'compression-webpack-plugin';
import zlib from 'zlib';
export default {
plugins: [
// gzip
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js| css |html| svg |json)$/,
threshold: 1024,
minRatio: 0.8,
}),
// Brotli
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js| css |html| svg |json)$/,
compressionOptions: { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 } },
threshold: 1024,
minRatio: 0.8,
}),
],
};
Резюме
Сжатие -- одна из самых простых и эффективных оптимизаций. Brotli сжимает лучше gzip на 15-25% для текста. Для статических файлов используй предварительное сжатие на максимальном уровне (Brotli 11), для динамических -- сжатие на лету с умеренным уровнем (Brotli 4-6). Не сжимай изображения, видео и другие бинарные форматы -- они уже сжаты.