HTTP Compression — gzip, Brotli, zstd in Spring Boot
Fast recall
Quick revision
Core answer
Content-Encoding. In Spring Boot enable it with server.compression.enabled=true, whitelist text MIME types via server.compression.mime-types, and set server.compression.min-response-size around 1–2 KB so small payloads don't pay CPU cost for no byte savings. Already-compressed assets (JPEG/PNG/MP4/Parquet) must be excluded — re-compressing them costs CPU and grows bytes.Whether you understand HTTP content-coding negotiation (Accept-Encoding/Content-Encoding), know the three mainstream codecs (gzip/br/zstd) and their trade-offs, and can configure Spring Boot compression with a sensible min-size threshold and MIME-type allow-list.
Enabling compression globally with `min-response-size=0`, compressing tiny JSON responses and paying CPU for kilobytes saved; or compressing already-compressed content types (images, video) and inflating size. Also: serving compressed responses without a `Vary: Accept-Encoding` header, poisoning shared caches.
Discuss Brotli static vs dynamic modes, zstd support (Chromium 123+), precompressed static asset serving via `gzip_static`/`brotli_static`, HTTP/2 HPACK + body compression composition, and why TLS 1.3 closed the BREACH attack only for specific cases — secrets in reflected bodies still need mitigation.
Interview-ready guide
Interview answer
HTTP compression is content-coding negotiated between client and server.
How negotiation works
- Client sends
Accept-Encoding: gzip, br, zstd— the list of codecs it can decode - Server picks one, compresses the body, and replies with
Content-Encoding: br - Server must add
Vary: Accept-Encodingso shared caches key per encoding; otherwise a CDN can serve a gzipped body to a client that only accepts identity
Three codecs that matter in 2026
- gzip — universal, cheap CPU, 3–5× on text; always safe
- Brotli — 15–30% better than gzip on text at comparable CPU; every modern browser supports it
- zstd — fastest, Chromium 123+ sends it; enable with brotli and gzip fallback
Spring Boot configuration
server.compression.enabled=trueserver.compression.mime-types— allow-list text and structured data (JSON, XML, HTML, CSS, JS, SVG, application/problem+json)server.compression.min-response-size: 2KB— below that the CPU cost outweighs the savings- Brotli is not built in — add
brotli4jas a servlet filter if you need it at origin
When compression hurts
- Already-compressed media: JPEG, PNG, WebP, MP4, Parquet — skip them, or you grow bytes
- Tiny payloads under 1 KB — gzip header alone is ~18 bytes
- Streaming endpoints (SSE, NDJSON) — flushing deflate state per chunk kills latency
- Secrets reflected in bodies with attacker-controlled input — BREACH attack surface
Static vs dynamic
- Static assets — precompress at build time with Brotli level 11 and gzip -9; serve
.br/.gzvariants from the CDN - Dynamic JSON — gzip level 6 or Brotli level 4; never level 11 on every request
CDN coordination
- If CloudFront, Fastly, or Cloudflare compresses at the edge, disable origin compression — don't double the work
- Verify with
curl -Ithrough the full chain thatContent-EncodingandVaryare correct
Verification
curl -H 'Accept-Encoding: br' -I— checkContent-EncodingandVary- Measure ratios per endpoint in monitoring; alert when a normally-compressed endpoint starts returning identity — usually a proxy stripping the header
HTTP Compression — gzip, Brotli, zstd in Spring Boot
Direct Answer
HTTP compression is content-coding negotiated via Accept-Encoding (client) and Content-Encoding (server). The server picks one codec — gzip (universal), Brotli (30–50% smaller on text, browser-only), or zstd (fast and small, Chromium-only) — compresses the body, and sets Content-Encoding. In Spring Boot enable it with server.compression.enabled=true, whitelist text MIME types via server.compression.mime-types, and set server.compression.min-response-size around 1–2 KB so small payloads don't pay CPU cost for no byte savings. Already-compressed assets (JPEG/PNG/MP4/Parquet) must be excluded — re-compressing them costs CPU and grows bytes.
Deep Dive & Architecture
HTTP caching has two main mechanisms: freshness (Cache-Control directives like max-age, public, private, no-cache, no-store, immutable) tell caches how long a response stays fresh without revalidation; validation (ETag, Last-Modified + If-None-Match/If-Modified-Since) lets a cache check with origin cheaply — 304 Not Modified if unchanged, saving bandwidth.
Use together: set Cache-Control: max-age=300, public for fresh reuse; set ETag so stale caches revalidate cheaply.
In Spring Boot: use ResponseEntity.ok().cacheControl(CacheControl.maxAge(5, TimeUnit.MINUTES)).eTag(etag).body(...) or ShallowEtagHeaderFilter for auto-ETags; combine with CDN (CloudFront, Cloudflare) and compression (Brotli > gzip) for real production wins.
Setting Cache-Control: no-cache everywhere (disables caching); returning wrong Vary headers (poisons CDN); generating weak ETags on dynamic data (no real savings)
Code Example
# 1. Ask for gzip explicitly
curl -s -I -H 'Accept-Encoding: gzip' https://api.example.com/v1/orders
# HTTP/2 200
# content-type: application/json
# content-encoding: gzip
# vary: Accept-Encoding
# content-length: 1847 # compressed size
# 2. Ask for brotli — modern CDNs will respect it
curl -s -I -H 'Accept-Encoding: br' https://api.example.com/v1/orders
# content-encoding: br
# vary: Accept-Encoding
# 3. Ask for multiple with q-values; server picks best
curl -s -I -H 'Accept-Encoding: zstd;q=1.0, br;q=0.9, gzip;q=0.8' \
https://api.example.com/v1/orders
# 4. Negotiation off -> identity response
curl -s -I -H 'Accept-Encoding: identity' https://api.example.com/v1/orders
# (no content-encoding header; raw body)
# 5. Decompress manually for debugging
curl -s -H 'Accept-Encoding: gzip' https://api.example.com/v1/orders | gunzip
# 6. Measure compression ratio on a real payload
for enc in identity gzip br; do
bytes=$(curl -s -H "Accept-Encoding: $enc" -o /dev/null -w '%{size_download}' \
https://api.example.com/v1/orders)
echo "$enc: $bytes bytes"
done
Common Mistakes & Pitfalls
Enabling compression globally with min-response-size=0, compressing tiny JSON responses and paying CPU for kilobytes saved; or compressing already-compressed content types (images, video) and inflating size. Also: serving compressed responses without a Vary: Accept-Encoding header, poisoning shared caches.
Check your understanding
Answer these without looking back at the article.
- Why is `Vary: Accept-Encoding` mandatory on compressed responses, and what breaks without it?
- How would you serve Brotli-compressed static assets from Spring Boot vs a CDN edge?
- When is BREACH still a concern in 2026, and how do you mitigate it?
- Why does compressing SSE/NDJSON endpoints usually make latency worse?
Related interview prep categories
1Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.