Zstandard Content-Encoding for the Web

This guide is part of Compression: Brotli & Zstandard, within Network & Server Response Optimization. Zstandard (zstd) is a compression algorithm designed for speed: it compresses and decompresses much faster than Brotli at comparable ratios in the low and middle levels, and it scales from very fast to very strong compression. Chromium-based browsers and Firefox now accept zstd as an HTTP Content-Encoding, advertising it in Accept-Encoding.

For static assets precompressed at build time, Brotli at level 11 still usually wins on size. Zstandard's advantage is in dynamic responses — server-rendered HTML and API JSON — where compression happens per request and CPU time adds to TTFB and server cost. There, zstd at levels 3–6 produces output close to Brotli 5–6 in size while using a fraction of the CPU, which matters for large responses and busy servers.

Dynamic compression of 1,000 SSR responses (avg 220KB) Bar chart of total CPU time to compress 1,000 server-rendered HTML responses with gzip, Brotli and zstd at typical dynamic levels. Dynamic compression of 1,000 SSR responses (avg 220KB) gzip 6 3.9s Brotli 5 3.3s zstd 3 0.9s Brotli 9 11.8s

Rapid Diagnosis

  • Check Accept-Encoding from your users' browsers in logs: count requests advertising zstd.
  • Check server and CDN support for zstd output.
  • Measure compression CPU for dynamic responses at your current settings.
  • Compare sizes: compress a sample of HTML and JSON responses with zstd and Brotli at candidate levels.

Root Cause Analysis: Why Consider zstd

1. CPU cost of dynamic compression. Large SSR pages compressed per request add CPU and TTFB.

2. Size vs speed trade-off. Brotli's best ratios come at slow levels unsuitable for dynamic content.

3. High-traffic APIs. JSON APIs with large payloads benefit from faster compression.

4. Growing browser support. A large share of users now accepts zstd.

Step-by-Step Resolution

1. Evaluate on your own responses

bash
# Compare sizes and times on a saved HTML response.
for l in 3 6 9; do /usr/bin/time -f "zstd -$l %es" zstd -q -$l -f -o page.zst page.html; ls -l page.zst | awk '{print $5}'; done
for q in 4 5 6; do /usr/bin/time -f "brotli -q $q %es" brotli -f -q $q -o page.br page.html; ls -l page.br | awk '{print $5}'; done
# trade-off: command-line timings include process startup; for small files, use
# a benchmark harness in your server language for realistic numbers.

2. Enable zstd with fallbacks

Configure the server or CDN to prefer zstd when advertised, then br, then gzip. Many servers select based on the client's Accept-Encoding list and their own preference order.

javascript
// Node.js sketch: choose encoding per request (zlib zstd support in recent Node versions).
import zlib from 'node:zlib';
function pickEncoding(ae = '') {
  if (ae.includes('zstd') && zlib.zstdCompress) return 'zstd';
  if (ae.includes('br')) return 'br';
  if (ae.includes('gzip')) return 'gzip';
  return null;
}
// trade-off: built-in zstd in Node.js is recent; on older runtimes use a native
// binding, or let a reverse proxy or CDN handle zstd instead.

3. Set Vary and cache keys

Send Vary: Accept-Encoding, and make sure CDN caches store zstd, br and gzip variants separately (or normalise to these buckets).

4. Keep static assets on Brotli 11

Precompressed static assets gain little from zstd; continue serving .br files to browsers that accept Brotli. Optionally add .zst files if your server prefers zstd.

Which encoding to serve Decision sequence for selecting Content-Encoding for a response. Which encoding to serve Is the response static and precompressed? Serve br (level 11) if accepted yes no Does the client accept zstd? Compress dynamically with zstd 3-6 yes no Does the client accept br? Compress dynamically with Brotli 4-5 yes no gzip

Verification

Check response headers from a browser that supports zstd: content-encoding: zstd for dynamic HTML and JSON. Compare server CPU usage and TTFB for dynamic routes before and after. Verify that clients without zstd still receive Brotli or gzip, and that CDN caches do not mix variants.

Worked Example: A Large E-commerce SSR Platform

An e-commerce platform server-rendered category pages averaging 310KB of HTML, compressed with Brotli level 6 at the origin. Compression accounted for 12% of server CPU and about 9ms of TTFB per request. Enabling zstd level 4 for browsers advertising it (about 70% of traffic) reduced compression CPU for those requests by 75%, saving 6–7ms of TTFB at p75 and allowing the team to reduce server instances by 8%. Transferred sizes were within 3% of Brotli 6. Brotli remained the fallback, and static assets continued to use precompressed Brotli 11.

Where zstd fits Recommended encoding by response type when Brotli and zstd are both available. Where zstd fits Response Preferred Fallback Hashed static JS and CSS Brotli 11 precompressed gzip 9 Static HTML from SSG Brotli 11 precompressed gzip 9 SSR HTML per request zstd 3-6 Brotli 4-5 then gzip Large JSON APIs zstd 3 Brotli 4 then gzip

zstd in the Wider Picture

Compression algorithm choice is a refinement. The biggest wins remain: compressing everything compressible at all, caching HTML so it is compressed once rather than per request, precompressing static assets at maximum levels, and shipping fewer bytes in the first place. Zstandard matters most for platforms with large dynamic responses and significant compression CPU. For a typical small site behind a CDN that compresses at the edge, enabling Brotli is the important step, and zstd can follow when the CDN supports it.

Rolling Out zstd Safely

Introduce zstd gradually. Enable it on a single dynamic route or a share of traffic, and compare three things against the Brotli control: response sizes, server CPU per request, and TTFB at p75 and p95. Watch error logs for decoding failures, which usually point to an intermediary that mishandles the unfamiliar encoding. Because the browser only receives zstd when it advertises support, and every other client keeps getting Brotli or gzip, rollback is a single configuration change.

Common Mistakes

  • Replacing Brotli for static assets. Brotli 11 usually produces smaller static files.
  • Forgetting fallbacks. Clients without zstd must still get Brotli or gzip.
  • Missing Vary. Caches could serve zstd to clients that cannot decode it.
  • High zstd levels on the fly. Levels above about 12 lose the speed advantage.

Edge Cases

Window size limits. Browsers limit the zstd window size they accept; standard server configurations stay within it.

Intermediary support. Older proxies may not recognise zstd and could mishandle it; test through your full delivery path.

Streaming. zstd supports streaming compression; flush per chunk for streaming SSR.

Dictionaries. zstd also supports dictionaries; see compression dictionary transport for the web standard.

FAQ

Which browsers support zstd Content-Encoding?

Chromium-based browsers and Firefox support it. Check current support data for Safari and others, and always keep fallbacks.

Is zstd better than Brotli?

For dynamic compression speed at similar ratios, often yes. For maximum compression of static assets, Brotli 11 usually produces smaller files.

What zstd level should I use?

Levels 3–6 for dynamic responses give a good balance. Higher levels slow down quickly without large size gains for typical web responses.

Does zstd decompress faster in the browser?

zstd decompression is very fast, but Brotli and gzip decompression are also fast enough that client-side differences are rarely noticeable.

Do CDNs support zstd?

Support is growing; some CDNs compress with zstd at the edge or pass through origin zstd responses. Check your provider's documentation.

Can I serve zstd over HTTP/1.1?

Content-Encoding is independent of the HTTP version, but browsers generally only advertise newer encodings over HTTPS.

Should API responses use zstd?

Large JSON responses are a good fit: they compress very well and benefit from fast compression on busy API servers.

Does zstd help Core Web Vitals directly?

Slightly, through lower TTFB on dynamic responses when compression time was significant. Its larger benefit is often reduced server CPU, which keeps latency stable under load.

Will search engine crawlers accept zstd?

Crawlers advertise the encodings they support; serve them whatever they accept, falling back to gzip.

Can zstd be precompressed too?

Yes. Static .zst files can be generated at build time and served to clients that accept zstd, although Brotli 11 is usually smaller for static text.