Brotli vs Gzip: Modern HTTP Compression Done Right (with Flask Examples)

Brotli vs Gzip: Modern HTTP Compression Done Right (with Flask Examples)

Learn when to use Brotli vs Gzip for HTTP compression. Complete implementation guide with practical examples and best practices for modern web performance.

Fast websites win. Compression is one of the cheapest performance gains you can get, and today the real choice is Brotli vs Gzip.

Short version:

  • Brotli → better compression, smaller files
  • Gzip → wider legacy support
  • Correct setup → Brotli first, Gzip fallback, plain text if all else fails

Let’s break it down and implement it properly.


What Is HTTP Compression?

HTTP compression reduces response size before sending it over the network.

Flow:

  1. Browser sends Accept-Encoding
  2. Server picks the best supported algorithm
  3. Response is compressed and sent with Content-Encoding

Smaller payload = faster load = better Core Web Vitals.


Brotli vs Gzip (Quick Comparison)

FeatureBrotliGzip
Compression ratio✅ Better❌ Lower
CPU costSlightly higherLower
Static assetsExcellentGood
Browser supportModern browsersUniversal
Default in HTTP/2/3CommonCommon

Rule of thumb:

  • Use Brotli when possible
  • Fallback to Gzip
  • Never break old clients

Browser Support (Client Side)

Modern browsers send this header automatically:

Accept-Encoding: br, gzip, deflate

Brotli Supported By

  • Chrome
  • Firefox
  • Edge
  • Safari
  • Mobile browsers (modern)

Gzip Supported By

  • Literally everything since the stone age

If br is present → use Brotli. If not → use Gzip. If neither → send uncompressed.


Required Response Headers

Compressed Response

Content-Encoding: br
Vary: Accept-Encoding

Why Vary Matters

Without it, CDNs and proxies may cache the wrong version and serve garbage.

Always include:

Vary: Accept-Encoding

Flask Implementation: Brotli + Gzip + Fallback

Install Dependencies

pip install brotli

Basic Flask Compression Middleware

from flask import Flask, request, Response
import gzip
import brotli

app = Flask(__name__)

def compress_response(data: bytes, encoding: str):
    if encoding == "br":
        return brotli.compress(data), "br"
    if encoding == "gzip":
        return gzip.compress(data), "gzip"
    return data, None

@app.after_request
def apply_compression(response: Response):
    if response.direct_passthrough:
        return response

    accept_encoding = request.headers.get("Accept-Encoding", "")
    data = response.get_data()

    encoding = None
    if "br" in accept_encoding:
        encoding = "br"
    elif "gzip" in accept_encoding:
        encoding = "gzip"

    compressed, used = compress_response(data, encoding)

    if used:
        response.set_data(compressed)
        response.headers["Content-Encoding"] = used
        response.headers["Content-Length"] = len(compressed)

    response.headers["Vary"] = "Accept-Encoding"
    return response

Serving Compressionless Responses (Opt-Out)

Some endpoints should not be compressed:

  • Streaming
  • SSE
  • Binary files
  • Already compressed data

Example:

@app.route("/health")
def health():
    response = Response("OK")
    response.direct_passthrough = True
    return response

Or explicitly disable compression via header:

response.headers["Content-Encoding"] = "identity"

Static Files: Best Practice

For production:

  • Precompress assets:

    • file.js.br
    • file.js.gz
  • Serve directly via CDN / reverse proxy

  • Flask should not handle this in production

But for local or internal tools, middleware is fine.


SEO Impact

Compression helps SEO indirectly:

  • Faster TTFB
  • Better LCP
  • Lower bandwidth usage
  • Higher Lighthouse scores

Google doesn’t care which compression you use — only that your site is fast.


Final Recommendation

Best setup today:

  1. Brotli for modern browsers
  2. Gzip fallback
  3. Plain response as last resort
  4. Always set Vary: Accept-Encoding
  5. Avoid compressing already-compressed data

If you’re using a CDN later, keep this logic consistent so behavior doesn’t change.

That's it. Simple, correct, future-proof.


Album of the blog: