All posts

Gzip, Deflate, and CompressionStream: Browser-Native Compression

July 27, 2026 · DevTools

gzip
compression
http
browser-api
developer-tools

Your server is sending a 400 KB JSON response. You enable gzip in your framework, and the transfer size drops to 60 KB. The browser decompresses it transparently. This works because the Accept-Encoding: gzip request header tells the server the client can handle it, and the Content-Encoding: gzip response header tells the client what to decompress.

But gzip is not the only compression format. Understanding what the three common formats are — and which one your tool uses — prevents confusion when debugging transport issues.

The GZip Compressor compresses and decompresses in your browser using the native CompressionStream API.

The three related formats

DEFLATE (RFC 1951) — the raw compression algorithm. Uses LZ77 (a dictionary-based compression that replaces repeated sequences with back-references) followed by Huffman coding. The format itself is a stream of compressed blocks. Most .zip files use DEFLATE internally.

zlib (RFC 1950) — DEFLATE with a 2-byte header and a 4-byte checksum (Adler-32) trailer. The wrapper that makes DEFLATE usable as a standalone stream format.

gzip (RFC 1952) — DEFLATE with a 10-byte header (including file metadata, OS type, and modification time) and an 8-byte trailer (CRC-32 checksum). Designed for single-file compression, commonly used for HTTP Content-Encoding.

gzip:   [10-byte header][DEFLATE stream][8-byte trailer]
zlib:   [2-byte header][DEFLATE stream][4-byte checksum]
deflate-raw: [DEFLATE stream] (no wrapper)

A gzip file decompresses to the same data as a zlib file decompresses to the same data as a deflate-raw stream — they all use the same LZ77+Huffman core. The wrappers differ.

When each format is used

  • gzip — HTTP Content-Encoding (most common). Most CDNs, reverse proxies (nginx, Cloudflare), and web servers (Apache, Caddy) support it.
  • zlib — PNG images use zlib compression. Many database backup formats use zlib. The zlib npm package produces zlib.
  • deflate-raw — some older HTTP implementations and specialty protocols. Rare in modern web development.

For browser-side compression, the CompressionStream API produces deflate-raw by default. To produce gzip-compatible output, use the gzip format argument.

Compression ratio: what to expect

The ratio depends on the data's entropy — its statistical randomness.

Data typeRaw sizegzippedRatio
HTML50 KB12 KB76%
JSON (structured)200 KB45 KB78%
JavaScript300 KB95 KB68%
Already compressed (JPEG)500 KB498 KB<1%

Text compresses well because of repeating strings, whitespace patterns, and keyword repetition. Already-compressed formats (JPEG, PNG, MP3) do not compress further.

The gzip level (1–9) trades CPU for size. Level 6 is the default in most servers. Level 9 (maximum compression) is rarely worth the CPU cost — the size savings over level 6 are marginal (~5%), but compression time increases significantly. Level 1 is fastest, useful for streaming.

The CompressionStream API

Since 2022, all modern browsers support the CompressionStream API natively, no library needed:

// Compress a string to gzip bytes
async function gzipCompress(text) {
  const cs = new CompressionStream("gzip");
  const writer = cs.writable.getWriter();
  writer.write(new TextEncoder().encode(text));
  writer.close();
  const reader = cs.readable.getReader();
  const chunks = [];
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value);
  }
  return new Uint8Array(
    chunks.reduce((acc, chunk) => acc + chunk.length, 0)
  );
}

// Decompress gzip bytes to string
async function gzipDecompress(bytes) {
  const cs = new DecompressionStream("gzip");
  const writer = cs.writable.getWriter();
  writer.write(bytes);
  writer.close();
  const reader = cs.readable.getReader();
  const decoder = new TextDecoder();
  let result = "";
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    result += decoder.decode(value);
  }
  return result;
}

Base64 transport for text formats

gzip output is binary. To transmit it as text (JSON payloads, email attachments, clipboard), encode it as Base64:

// Compress and Base64-encode for transport
const compressed = await gzipCompress(largeJSONString);
const base64 = btoa(String.fromCharCode(...compressed));
// Send as { data: base64 }

To decompress, Base64-decode first, then pass to DecompressionStream.

Server-side considerations

Modern servers compress automatically. Key settings to verify:

# nginx — enable gzip
gzip on;
gzip_types application/json text/css application/javascript;
gzip_min_length 1000;   # don't compress tiny responses
// Express — compression middleware
import compression from "compression";
app.use(compression({ level: 6 }));

The GZip Compressor runs entirely in the browser — no server required. Compress a file, download the result, or decompress a gzip payload for inspection. Use it to test what a compressed response looks like before implementing server-side compression, or to inspect gzip files from a CI artifact.