zlib.deflateSync()

Compresses data synchronously into the raw zlib (DEFLATE) format and returns it as a Buffer.

Signature
zlib.deflateSync(buffer[, options])
  • Added in Node.js v0.11.3

Parameters

ParameterTypeDescription
$bufferBuffer | TypedArray | DataView | ArrayBuffer | stringThe data to compress. A string is encoded as UTF-8 first.
$optionsoptionalObjectzlib options: `level`, `strategy`, `memLevel`, `windowBits`, `chunkSize`, `flush`, `finishFlush`, `dictionary`, `info` and `maxOutputLength`.

Return value

BufferA Buffer containing the zlib-format compressed data. With `options.info` set to `true`, an object `{ buffer, engine }` is returned instead.

Errors

Throws synchronously on invalid options or compression failure, and `ERR_BUFFER_TOO_LARGE` when the result exceeds `maxOutputLength`.

Changelog

  • PHP v5.11.0The `finishFlush` option is supported.
  • PHP v8.0.0The `dictionary` option can be a `Uint8Array`.
  • PHP v9.4.0The `dictionary` option can be an `ArrayBuffer`.
  • PHP v14.5.0, v12.19.0The `maxOutputLength` option is supported.

zlib.deflateSync() produces a zlib-format stream as defined by RFC 1950: a two-byte header (commonly 78 9c), the DEFLATE-compressed payload, and a four-byte Adler-32 checksum. Compared with gzip, that is roughly 11 bytes less framing per payload — gzip additionally stores a magic number, a modification time, an OS byte and a CRC32 plus length trailer.

When to reach for it

Pick zlib format when both ends are your own code and every byte matters: many small values in a cache, rows in a database column, records in a custom binary file format. It is also the format required by several specs — PNG chunks, PDF FlateDecode streams, the ZIP container, and Content-Encoding: deflate.

Pick gzipSync() instead whenever the output will leave your process: a file someone might open with gunzip, or a body served to a browser. The extra bytes buy interoperability and a stronger checksum.

What trips people up

  • Three DEFLATE variants exist and they are not interchangeable. deflateSync() writes zlib framing, deflateRawSync() writes bare DEFLATE with no header at all, and gzipSync() writes gzip framing. Decompress with the matching function or you get Z_DATA_ERROR.
  • Browsers historically disagreed about what Content-Encoding: deflate means, some expecting raw DEFLATE. Serving gzip or Brotli avoids the ambiguity entirely.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Compress many small cache values where 11 bytes of gzip header per entry is significant
  • Build a PNG or PDF writer that needs RFC 1950 FlateDecode streams
  • Store compressed blobs in a database column read only by your own code
  • Compare framing overhead against gzip before choosing a storage format

Frequently asked questions

What is the difference between deflateSync and gzipSync?

Both compress with DEFLATE, but deflateSync writes an 11-byte-smaller zlib (RFC 1950) wrapper with an Adler-32 checksum, while gzipSync writes the gzip wrapper with a CRC32 that command-line tools and browsers understand. Use gzip for anything leaving your process.

Why do I get incorrect header check when decompressing deflate data?

You are using the wrong decompressor for the framing — deflateSync output needs inflateSync(), deflateRawSync output needs inflateRawSync(), and gzipSync output needs gunzipSync(). The three formats are not interchangeable.

How do I get the smallest possible output from deflateSync?

Pass level: 9 with strategy tuned to your data, and for many short similar payloads supply a shared dictionary so repeated structure compresses away. For text, brotliCompressSync() usually beats even level 9 DEFLATE.

More examples

A shared dictionary makes small payloads much smaller

The dictionary preloads the compressor's window with the JSON keys that repeat across every record, so those bytes are encoded as back-references instead of literals. This is the standard trick for compressing many short, structurally similar messages, where a single record on its own is too small for DEFLATE to find any redundancy.

Example · javascript
const zlib = require('node:zlib');

// Both sides agree on this dictionary out of band.
const dictionary = Buffer.from('{"userId":,"eventType":"","timestamp":,"sessionId":""}');

const record = Buffer.from(JSON.stringify({
  userId: 4821, eventType: 'click', timestamp: 1700000000, sessionId: 'a1b2c3'
}));

const plain = zlib.deflateSync(record);
const primed = zlib.deflateSync(record, { dictionary });

console.log('raw       :', record.length, 'bytes');
console.log('deflate   :', plain.length, 'bytes');
console.log('w/ dict   :', primed.length, 'bytes');

// The same dictionary is mandatory when decompressing.
console.log('decoded:', zlib.inflateSync(primed, { dictionary }).toString());

Store compressed blobs with a format marker

A one-byte tag records whether the payload was worth compressing. Without it, short values would be stored larger than the original, and the reader would have no way to tell which branch produced a given blob. This is roughly what production caches do internally.

Example · javascript
const zlib = require('node:zlib');

function pack(text) {
  const raw = Buffer.from(text, 'utf8');
  const deflated = zlib.deflateSync(raw, { level: 9 });
  // Only pay for compression when it actually wins.
  return deflated.length < raw.length
    ? Buffer.concat([Buffer.from([1]), deflated])
    : Buffer.concat([Buffer.from([0]), raw]);
}

function unpack(blob) {
  const body = blob.subarray(1);
  return (blob[0] === 1 ? zlib.inflateSync(body) : body).toString('utf8');
}

const big = pack('repeated payload '.repeat(40));
const tiny = pack('hi');

console.log('big  ->', big[0] === 1 ? 'compressed' : 'stored', big.length, 'bytes');
console.log('tiny ->', tiny[0] === 1 ? 'compressed' : 'stored', tiny.length, 'bytes');
console.log('round trips:', unpack(big).length, unpack(tiny));

Discussion

  • Be the first to comment on this lesson.