Как работает сжатие HTTP-ответов: gzip, brotli и Content-Encoding?
HTTP и HTTPS
Клиент в `Accept-Encoding` перечисляет поддерживаемые алгоритмы, сервер выбирает один, сжимает тело и указывает его в `Content-Encoding`; для текста это экономит 60–90% трафика.
Алгоритмы
- gzip (DEFLATE) — поддерживается везде, быстрый, уровни 1–9.
- brotli (
br) — для текста обычно на 15–25% компактнее gzip благодаря встроенному словарю. Уровни 0–11: 11 очень медленный, поэтому статику сжимают заранее, а на лету берут 4–6. Браузеры объявляютbrтолько по HTTPS. - zstd — быстрый, уже поддержан Chrome и Firefox.
Что сжимать
- HTML, CSS, JS, JSON, SVG — да.
- JPEG, PNG, WebP, видео, архивы — нет: они уже сжаты, получите лишний CPU и порой больше байт.
- Ответы меньше ~1 КБ — выигрыш не окупает накладные расходы.
// сжать тело запроса (серверу нужен gunzip)
const json = new Blob([JSON.stringify(Array(1000).fill({ status: 'active' }))]);
const gz = await new Response(json.stream().pipeThrough(new CompressionStream('gzip'))).blob();
console.log(json.size, gz.size); // JSON сжимается в разы
await fetch('/api/items', { method: 'POST', body: gz, headers: { 'Content-Encoding': 'gzip' } });
for (const e of performance.getEntriesByType('resource')) // сжатие ответов
if (e.encodedBodySize) console.log(e.name, (e.decodedBodySize / e.encodedBodySize).toFixed(1));Подводные камни
- Без
Vary: Accept-EncodingCDN может отдать brotli клиенту, который его не понимает. Content-Length— это размер сжатого тела.- BREACH: если в сжатом ответе секрет соседствует с данными атакующего, секрет угадывается по длине ответа даже через HTTPS.
Коротко для собеседования
Сервер сжимает только тем алгоритмом, который клиент объявил в Accept-Encoding: текст — brotli или gzip, уже сжатые картинки и видео не трогают. Лучше делать это на прокси или CDN, статику класть заранее сжатой и не забывать Vary: Accept-Encoding.