---
title: "Brotli vs Gzip: Modern HTTP Compression Done Right (with Flask Examples)"
description: "Learn when to use Brotli vs Gzip for HTTP compression. Complete implementation guide with practical examples and best practices for modern web performance."
slug: "brotli-vs-gzip-modern-http-compression-done-right-with-flask-examples"
url: "https://furkanbaytekin.dev/blogs/brotli-vs-gzip-modern-http-compression-done-right-with-flask-examples"
author: "Furkan Baytekin"
category: "Software"
tags: []
published: 2025-12-21T10:15:28Z
updated: 2025-12-21T10:15:28Z
readTime: 3
cover: "https://myblogcms.furkanbaytekin.dev/uploads/cover/brotli-vs-gzip-modern-http-compression-done-right-with-flask-examples+1766311193632"
---

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)

| Feature             | Brotli          | Gzip      |
| ------------------- | --------------- | --------- |
| Compression ratio   | ✅ Better       | ❌ Lower  |
| CPU cost            | Slightly higher | Lower     |
| Static assets       | Excellent       | Good      |
| Browser support     | Modern browsers | Universal |
| Default in HTTP/2/3 | Common          | Common    |

Rule of thumb:

- **Use Brotli when possible**
- **Fallback to Gzip**
- **Never break old clients**

---

## Browser Support (Client Side)

Modern browsers send this header automatically:

```http
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

```http
Content-Encoding: br
Vary: Accept-Encoding
```

### Why `Vary` Matters

Without it, CDNs and proxies may cache the wrong version and serve garbage.

Always include:

```http
Vary: Accept-Encoding
```

---

## Flask Implementation: Brotli + Gzip + Fallback

### Install Dependencies

```bash
pip install brotli
```

---

### Basic Flask Compression Middleware

```python
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:

```python
@app.route("/health")
def health():
    response = Response("OK")
    response.direct_passthrough = True
    return response
```

Or explicitly disable compression via header:

```python
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:

<iframe data-testid="embed-iframe" style="border-radius:12px" src="https://open.spotify.com/embed/album/0LBQdWnuV0CAXyPIngb0UX?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
