zlib.brotliDecompressSync()

Decompresses Brotli-compressed data synchronously and returns the original bytes as a Buffer.

Signature
zlib.brotliDecompressSync(buffer[, options])
  • Added in Node.js v11.7.0, v10.16.0

Parameters

ParameterTypeDescription
$bufferBuffer | TypedArray | DataView | ArrayBuffer | stringBrotli-compressed data to expand.
$optionsoptionalObjectBrotli options: `flush`, `finishFlush`, `chunkSize`, `params`, `info` and `maxOutputLength`.

Return value

BufferA Buffer holding the decompressed data. With `options.info` set to `true`, an object `{ buffer, engine }` is returned instead.

Errors

Throws synchronously when the input is not a valid Brotli stream or is truncated, and `ERR_BUFFER_TOO_LARGE` when the output exceeds `maxOutputLength`.

Changelog

  • PHP v14.5.0, v12.19.0The `maxOutputLength` option is supported, capping how large the decompressed result may grow.

zlib.brotliDecompressSync() reverses zlib.brotliCompressSync() and reads any well-formed Brotli stream, including .br files produced by other tools and response bodies served as Content-Encoding: br.

Decompression speed does not depend on quality

This is the useful asymmetry in Brotli. Whether the encoder used quality 1 or quality 11, decoding costs roughly the same and is fast — comparable to gzip. That is exactly why precompressing static assets at quality 11 is worth it: you pay once at build time and every client decodes cheaply.

What trips people up

  • No magic bytes. Unlike gzip's 1f 8b, a Brotli stream has no reliable format signature. You cannot sniff it — you have to know from context, a file extension, or a Content-Encoding header. Feeding it gzip data throws, but the error message is about internal stream format rather than a clean header mismatch.
  • Unbounded expansion. Brotli's high ratios make decompression bombs cheap to build. Set maxOutputLength on anything you did not compress yourself so an oversized result raises ERR_BUFFER_TOO_LARGE.
  • Blocking. Decoding is fast, but on a multi-megabyte payload the sync call still holds the event loop. Use zlib.brotliDecompress() or zlib.createBrotliDecompress() in a server.

Example

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

When to use it

  • Read a .br asset back in a build verification step
  • Expand a Brotli-compressed record fetched from object storage
  • Decode a captured Content-Encoding: br body while debugging an API
  • Confirm in a test that a Brotli compression helper round-trips exactly

Frequently asked questions

How do I decompress Brotli data in Node.js?

Call zlib.brotliDecompressSync(buffer) for data already in memory, or pipe through zlib.createBrotliDecompress() for a stream. Both are built into the zlib module with no dependency required.

How can I tell whether a buffer is Brotli or gzip?

You cannot detect Brotli by inspection because its streams have no magic-byte signature, unlike gzip which always starts with 1f 8b. Check for the gzip signature first and treat everything else as Brotli only when the file extension or Content-Encoding header says so.

Is Brotli decompression slower than gzip?

No — Brotli decoding is comparable to gzip and, crucially, does not get slower when the encoder used a higher quality setting. Only compression time scales with quality.

More examples

Decoding cost is flat across encoder quality

The compressed sizes differ across quality levels but the decode times stay in the same narrow band. That asymmetry is the argument for spending build-time CPU at quality 11: readers never pay for it.

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

const payload = Buffer.from('lorem ipsum dolor sit amet '.repeat(4000));

for (const q of [1, 5, 11]) {
  const packed = zlib.brotliCompressSync(payload, {
    params: { [zlib.constants.BROTLI_PARAM_QUALITY]: q }
  });

  const t0 = process.hrtime.bigint();
  const out = zlib.brotliDecompressSync(packed);
  const ms = Number(process.hrtime.bigint() - t0) / 1e6;

  console.log(`encoded q${q}: ${packed.length} bytes, decoded in ${ms.toFixed(2)} ms, ok=${out.equals(payload)}`);
}

Read a compressed asset back with a size guard

A build-verification pattern: compress at maximum quality, then read the artifact back and assert it decodes to the exact source. The maxOutputLength cap is cheap insurance even for your own files, since a corrupted artifact should fail loudly rather than allocate.

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

// Produce the artifact a build step would have written.
const source = 'export const config = { retries: 3 };\n'.repeat(200);
fs.writeFileSync('bundle.js.br', zlib.brotliCompressSync(source, {
  params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 }
}));

const onDisk = fs.readFileSync('bundle.js.br');
const restored = zlib.brotliDecompressSync(onDisk, {
  maxOutputLength: 5 * 1024 * 1024
});

console.log('on disk:', onDisk.length, 'bytes');
console.log('decoded:', restored.length, 'bytes');
console.log('matches source:', restored.toString() === source);

fs.unlinkSync('bundle.js.br');

Discussion

  • Be the first to comment on this lesson.