Gzip, Deflate, and CompressionStream: Browser-Native Compression
July 27, 2026 · DevTools
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
zlibnpm 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 type | Raw size | gzipped | Ratio |
|---|---|---|---|
| HTML | 50 KB | 12 KB | 76% |
| JSON (structured) | 200 KB | 45 KB | 78% |
| JavaScript | 300 KB | 95 KB | 68% |
| Already compressed (JPEG) | 500 KB | 498 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.