Skip to content
Journal

Technology · Performance

Zstandard vs LZ4 vs gzip: Which Compression to Use

Use zstd as your default, LZ4 when decompression speed beats size, gzip only for compatibility. Benchmark numbers, code and a decision table for 2026.

Shashikant Gupta

Shashikant Gupta

5 min read

A thick stack of paper sheets pressed under a brass hand press with a wooden handle

Sponsored

Share

Use Zstandard as your default compression algorithm in 2026. Reach for LZ4 when you need data back out as fast as memory allows, and keep gzip for places where you can’t control the other end of the wire. That’s the short version. The longer version depends on one question: where do the compressed bytes go, and who waits for them?

The numbers

The zstd project publishes an lzbench comparison on its README. It was run on an Intel Core i7-9700K at 4.9 GHz under Ubuntu 24.04, using the Silesia corpus. Treat the figures as relative, not as what your hardware will do.

CompressorRatioCompressionDecompression
zstd 1.5.7 -12.896510 MB/s1550 MB/s
brotli 1.1.0 -12.883290 MB/s425 MB/s
zlib 1.3.1 -12.743105 MB/s390 MB/s
zstd 1.5.7 —fast=12.439545 MB/s1850 MB/s
zstd 1.5.7 —fast=42.146665 MB/s2050 MB/s
lz4 1.10.02.101675 MB/s3850 MB/s
snappy 1.2.12.089520 MB/s1500 MB/s

Read that table twice. Against zlib level 1 (the algorithm inside gzip), zstd level 1 gets a better ratio, compresses nearly five times faster and decompresses about four times faster. There is no trade-off to weigh. It is simply better on this test.

LZ4 is the odd one out. Its ratio is worse than zstd at any level shown, yet it decompresses at 3850 MB/s, about 2.5 times faster than zstd level 1. That gap is the whole reason LZ4 still exists.

Pick by where the bytes go

Ratio matters when bytes are expensive to move or store. Decompression speed matters when bytes are read constantly and someone is waiting.

SituationChoiceWhy
Backups, build artifacts, logs archived to object storagezstd, higher levelWritten once, read rarely. Spend CPU once for a smaller bill forever.
API responses and service-to-service trafficzstd, level 1-3Fast enough to run inline on every request, with a better ratio than gzip.
Database pages, in-memory cachesLZ4Every read pays decompression, so speed wins over a few extra percent of size.
Small metadata and index recordsLZ4Lookups are latency-bound.
Public HTTP responses to unknown clientsgzip or Brotli, zstd where supportedNegotiate with Accept-Encoding and fall back.
Files sent to a partner whose tool only reads .gzgzipCompatibility is the only requirement.

A real example of mixing: Next.js 16.4’s Turbopack disk cache got 20-25% smaller. The release notes say it now uses Zstandard for the data that takes up most of the space, and keeps LZ4 for metadata to preserve fast cache lookups. Bulk data takes the ratio. Lookups take the speed. We covered the rest of that release in Next.js 16.4: Cache Components Are Now the Default.

Trying it in code

Recent Node.js releases include zstd in node:zlib, so you can compare on your own data without installing anything:

import { gzipSync, zstdCompressSync, zstdDecompressSync } from 'node:zlib';
import { readFileSync } from 'node:fs';

const data = readFileSync('./sample.json'); // use your real payloads

const t0 = performance.now();
const gz = gzipSync(data, { level: 1 });
const t1 = performance.now();
const zs = zstdCompressSync(data);
const t2 = performance.now();

console.log('gzip', gz.length, `${(t1 - t0).toFixed(1)} ms`);
console.log('zstd', zs.length, `${(t2 - t1).toFixed(1)} ms`);
console.log('roundtrip ok', zstdDecompressSync(zs).equals(data));

Python 3.14 added compression.zstd to the standard library, and the same idea applies there. On the command line, zstd -3 file and zstd -19 file show the level trade-off in about ten seconds.

Run it on your own payloads. JSON with repeated keys, base64 blobs, already-compressed images and logs all behave differently. Compressing a JPEG with any of these saves almost nothing and burns CPU.

Mistakes I see most often

Compressing before encrypting is fine; compressing after mixing secrets with attacker input is not. Compression ratio can leak information when an attacker controls part of the plaintext and can observe the compressed size, which is the idea behind the CRIME and BREACH attacks on HTTPS. Don’t compress responses that reflect secrets alongside user-supplied data without thinking it through.

Choosing the highest level by default. Top zstd levels are for data you write once. On a request path they will be your slowest line.

Compressing tiny payloads. For records of a few hundred bytes, headers and dictionary warm-up cancel the gain. zstd supports trained dictionaries for exactly this case, and that is worth testing if you store millions of small similar objects.

Forgetting the fallback. If you serve zstd over HTTP, honor Accept-Encoding and keep a gzip or Brotli path. Not every client or intermediary handles zstd yet.

How to decide

Make a ten-minute test, not a debate. Take a sample of your real data, run gzip level 1, zstd level 1 and 3, and LZ4 on it, and record size, compression time and decompression time. Then ask who waits on each number. If it’s a user, optimize the slower of the two directions. If it’s a bill, optimize size.

The takeaway: start with zstd, switch to LZ4 for hot read paths, and leave gzip for the edges you don’t own. If you design storage layers or write-ahead logs, put this on the checklist; for index-heavy systems, it pairs well with how LSM trees compress their sorted files.

Frequently asked questions

Is zstd better than gzip?
For most new work, yes. In the zstd project's benchmark, zstd level 1 reaches a 2.896 ratio at 510 MB/s compression and 1550 MB/s decompression, against 2.743, 105 MB/s and 390 MB/s for zlib level 1. gzip still wins on compatibility, since every browser, proxy and tool understands it.
When should I use LZ4 instead of zstd?
When you read compressed data far more often than you write it and the read path is latency-sensitive: database pages, in-memory caches, small metadata records. LZ4 decompressed at 3850 MB/s in the benchmark, roughly 2.5 times zstd level 1, at the cost of a lower ratio.
What zstd level should I use?
Start at the default level 3 for general use and test level 1 if CPU is the constraint. Higher levels give smaller output but compress much more slowly, so reserve them for data you write once and read many times, like build artifacts or backups.
Does decompression get slower at higher zstd levels?
Not much. Decompression speed stays high across levels, which is one reason zstd suits write-once, read-many data: you pay at compression time and read quickly forever.
Can browsers use zstd for HTTP responses?
Support for zstd as an HTTP content encoding exists in recent browsers, but check current compatibility tables for the clients you serve. Keep gzip or Brotli as a fallback and negotiate with the Accept-Encoding header.

Sources

Sponsored

Sponsored

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored