⚠️ Maintenance. Notice: the site is currently in maintenance mode, so the amount of available material is temporarily limited.
Home / Tutorials / CompressionСжатие

Compression with permessage-deflateСжатие через permessage-deflate

What permessage-deflate isЧто такое permessage-deflate

permessage-deflate (RFC 7692) is the standard WebSocket extension that compresses each message with DEFLATE — the same algorithm behind gzip. For repetitive text payloads like JSON it can cut bytes on the wire by 70–90%, which matters on mobile networks and high-fan-out broadcasts. It is negotiated once during the handshake and is then transparent: your send() and onmessage code does not change.

permessage-deflate (RFC 7692) — стандартное расширение WebSocket, сжимающее каждое сообщение алгоритмом DEFLATE — тем же, что и за gzip. Для повторяющихся текстовых данных вроде JSON оно сокращает объём «на проводе» на 70–90%, что важно в мобильных сетях и при широковещании на много клиентов. Оно согласуется один раз при рукопожатии и далее прозрачно: ваш код send() и onmessage не меняется.

Negotiation in the handshakeСогласование при рукопожатии

The client offers the extension via a header; the server accepts it (optionally with parameters) or stays silent to decline. Browsers offer it automatically, so most of the control lives on the server.

handshake
// client → server (offer)
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

// server → client (accept, here without context takeover)
Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover

Клиент предлагает расширение заголовком; сервер принимает его (по желанию с параметрами) или молчит, чтобы отклонить. Браузеры предлагают его автоматически, поэтому почти всё управление — на сервере.

handshake
// клиент → сервер (предложение)
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

// сервер → клиент (принятие, здесь без context takeover)
Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover

Enabling it in Node.jsВключение в Node.js

The ws library exposes the extension through the perMessageDeflate option. It is off by default in recent versions because the defaults can be memory-hungry — so configure it deliberately:

server.js
import { WebSocketServer } from "ws";

const wss = new WebSocketServer({
  port: 8080,
  perMessageDeflate: {
    threshold: 1024,            // skip compressing messages < 1 KiB
    serverNoContextTakeover: true,  // reset dictionary per message
    clientNoContextTakeover: true,
    concurrencyLimit: 10,         // limit parallel zlib jobs
    zlibDeflateOptions: { level: 6, memLevel: 7 }
  }
});
The threshold is the single most useful knob: tiny frames cost more in CPU and framing overhead than they save, so don't compress them at all.

Библиотека ws предоставляет расширение через опцию perMessageDeflate. В свежих версиях оно выключено по умолчанию, потому что настройки по умолчанию могут быть прожорливы к памяти — поэтому конфигурируйте осознанно:

server.js
import { WebSocketServer } from "ws";

const wss = new WebSocketServer({
  port: 8080,
  perMessageDeflate: {
    threshold: 1024,            // не сжимать сообщения < 1 КиБ
    serverNoContextTakeover: true,  // сбрасывать словарь на каждое сообщение
    clientNoContextTakeover: true,
    concurrencyLimit: 10,         // предел параллельных задач zlib
    zlibDeflateOptions: { level: 6, memLevel: 7 }
  }
});
Параметр threshold — самая полезная «ручка»: крошечные кадры стоят дороже в CPU и накладных, чем экономят, поэтому их не сжимают вовсе.

Context takeover: the memory trade-offContext takeover: компромисс по памяти

“Context takeover” means the DEFLATE dictionary is kept between messages, so repeated patterns compress better over time. The catch: each connection then holds a zlib context — easily tens of kilobytes — and with tens of thousands of sockets that adds up to real RAM.

  • Context takeover on — best compression ratio, highest memory. Good for a few long-lived, chatty connections.
  • no_context_takeover — the dictionary resets each message; slightly worse ratio, dramatically less memory. Usually the right call at scale.

«Context takeover» означает, что словарь DEFLATE сохраняется между сообщениями, поэтому повторяющиеся паттерны со временем сжимаются лучше. Подвох: каждое соединение тогда держит контекст zlib — легко десятки килобайт — и при десятках тысяч сокетов это превращается в ощутимую RAM.

  • Context takeover включён — лучший коэффициент сжатия, максимум памяти. Хорошо для немногих долгоживущих «болтливых» соединений.
  • no_context_takeover — словарь сбрасывается на каждое сообщение; чуть хуже коэффициент, кардинально меньше памяти. Обычно правильный выбор при масштабе.

When to enable itКогда включать

  • Do enable for large, repetitive text (JSON, logs, markup) over constrained networks.
  • Don't bother for already-compressed payloads — images, video, gzip blobs or tightly packed binary frames. You'll burn CPU for almost no gain.
  • Measure both bytes saved and CPU/memory before and after. Compression is a trade, not a free win.

If you're broadcasting the same payload to many clients, also see scaling WebSockets — compressing once per unique message rather than per recipient is a bigger lever than the deflate level.

  • Включайте для крупного повторяющегося текста (JSON, логи, разметка) в ограниченных сетях.
  • Не стоит для уже сжатых данных — изображения, видео, gzip-блобы или плотно упакованные бинарные кадры. Сожжёте CPU почти без выгоды.
  • Измеряйте и сэкономленные байты, и CPU/память до и после. Сжатие — это компромисс, а не бесплатный выигрыш.

Если вы рассылаете одни и те же данные многим клиентам, см. также масштабирование WebSocket — сжать один раз на уникальное сообщение, а не на каждого получателя, важнее, чем уровень deflate.