gzipbrotliweb performancecompression

Gzip and Brotli: how HTTP compression works and how to enable it

HTTP compression explained: LZ77, Huffman, Brotli windows, compression levels, what to compress and what not, plus Nginx and CDN configuration.

August 25, 2026·7 min read

The text you download from a website travels compressed to 20-30% of its original size. That invisible compression —negotiated between browser and server in one header— is among the highest-impact optimizations per effort invested, yet many projects still have it misconfigured or disabled. Here's the full mechanism and its fine-tuning.

Negotiation in two headers

The whole protocol fits in four lines:

# The browser announces support, in priority order:
Accept-Encoding: gzip, deflate, br, zstd

# The server answers with its choice:
Content-Encoding: br

If the server doesn't respond with Content-Encoding, the resource travels uncompressed — exactly what to audit when you suspect something's off. Modern browsers support all four: gzip (universal), deflate (zlib, rare today), br (Brotli) and zstd (Zstandard, the newcomer).

The shared engine: LZ77 + Huffman

Gzip and Brotli share two fundamental ideas inherited from DEFLATE:

LZ77 (repetition substitution): when a previously seen sequence appears again, emit a backward reference —"copy 25 bytes from 340 positions ago"— instead of repeating it. HTML/CSS/JS repeat constantly: tags, class names, keywords, selectors.

Huffman (variable-length codes): frequent symbols get short codes, rare ones long. The letter "e" may cost 4 bits; an uncommon character 15.

The difference between formats lies in how much each piece gets optimized:

Gzip Brotli
LZ77 window 32 KB up to 16 MB
Huffman codes dynamic per block multiple contexts
Built-in dictionary No Yes (120 KB common web)
Typical ratio vs gzip reference 15-25% smaller

Brotli's large window matters for big files: a repetition 500 KB apart is invisible to gzip but caught by brotli. And the built-in dictionary —frequent HTML, CSS, JS fragments and common English words— means even a tiny 2 KB file compresses as if half of it had been seen before.

Zstd adds another dimension: near-brotli ratios with much faster compression speed, ideal where CPU matters (CDNs re-compressing on the fly). Its web adoption is growing fast.

Compression levels: the real trade-off

Both formats offer levels (gzip 1-9, brotli 0-11). Higher level = more CPU spent searching better encodings = smaller file. The curve is harsh:

  • Gzip level 6 → 1: file grows only ~5% while compressing 10× faster.
  • Brotli level 11 vs 5: saves ~3-6% more and can take 10-50× longer.

The professional strategy: static compression — compress at build time (brotli -11 everything) and serve prepared .br files. Maximum level costs nothing at runtime. Dynamic compression (per request) stays for live-generated content, at medium levels.

What to compress and what not

High compression: HTML, CSS, JS, SVG, JSON, XML, text — typical ratios 70-85%. A 900 KB JS bundle drops to ~250 KB gzipped / ~200 KB brotli.

Don't compress (already compressed internally): JPG, PNG, WebP, AVIF, MP4, WebM, PDF, WOFF2. Re-compressing burns CPU to gain <1% and can even worsen things. Notable: WOFF2 already ships internally brotli-compressed — that's why it weighs so little.

One critical interaction: compression doesn't replace minification, it complements it. Minifying removes bytes (whitespace, comments, long names); compression encodes the rest better. Minified code also compresses better because repeated symbols concentrate. Correct pipeline: minify → compress. Our code minifier covers step one locally.

Practical configuration

Nginx (native gzip; brotli needs the ngx_brotli module):

gzip on;
gzip_types text/plain text/css application/json application/javascript image/svg+xml;
gzip_min_length 1024;
gzip_comp_level 6;

# With ngx_brotli:
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/json application/javascript image/svg+xml;

If you serve from Vercel/Netlify/Cloudflare this comes done and well-tuned — one more reason for the modern edge. On your own servers, verify after configuring:

curl -sI -H "Accept-Encoding: br,gzip" https://mydomain.com/ | grep -i content-encoding

No content-encoding back? Something's disabled or the MIME type isn't listed — classic error: serving .svg without declaring image/svg+xml in gzip_types.

FAQ

Compress dynamic responses? Yes for API JSON/text, at medium level and watching generated latency. For rendered HTML evaluate: if TTFB is already tight, on-the-fly compression may hurt more than help — cache first.

Does Brotli work only over HTTPS? Browsers only announce br on secure connections. Plain HTTP gets gzip. In 2026 every serious site runs HTTPS anyway.

How much does zstd improve over brotli? Similar ratios (±3%); zstd clearly wins compression speed. For pre-compressed statics the difference is irrelevant; for massive on-the-fly compression, zstd is the future.


Minify your HTML, CSS and JS before compressing with our online minifier, free and right in your browser.

Try it without code

Code Minifier

Minify HTML, CSS and JS.

Open Code Minifier

Built by

Miguel Ángel Colorado Marin (MACM)

Full-Stack Developer · Guadalajara, España

I develop web apps, digital tools and full projects — from design to deployment.

Contact me