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
5 min read
Sponsored
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.
| Compressor | Ratio | Compression | Decompression |
|---|---|---|---|
| zstd 1.5.7 -1 | 2.896 | 510 MB/s | 1550 MB/s |
| brotli 1.1.0 -1 | 2.883 | 290 MB/s | 425 MB/s |
| zlib 1.3.1 -1 | 2.743 | 105 MB/s | 390 MB/s |
| zstd 1.5.7 —fast=1 | 2.439 | 545 MB/s | 1850 MB/s |
| zstd 1.5.7 —fast=4 | 2.146 | 665 MB/s | 2050 MB/s |
| lz4 1.10.0 | 2.101 | 675 MB/s | 3850 MB/s |
| snappy 1.2.1 | 2.089 | 520 MB/s | 1500 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.
| Situation | Choice | Why |
|---|---|---|
| Backups, build artifacts, logs archived to object storage | zstd, higher level | Written once, read rarely. Spend CPU once for a smaller bill forever. |
| API responses and service-to-service traffic | zstd, level 1-3 | Fast enough to run inline on every request, with a better ratio than gzip. |
| Database pages, in-memory caches | LZ4 | Every read pays decompression, so speed wins over a few extra percent of size. |
| Small metadata and index records | LZ4 | Lookups are latency-bound. |
| Public HTTP responses to unknown clients | gzip or Brotli, zstd where supported | Negotiate with Accept-Encoding and fall back. |
Files sent to a partner whose tool only reads .gz | gzip | Compatibility 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