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:
-
Browser sends
Accept-Encoding - Server picks the best supported algorithm
-
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:
httpAccept-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
httpContent-Encoding: br
Vary: Accept-Encoding
Why Vary Matters
Without it, CDNs and proxies may cache the wrong version and serve garbage.
Always include:
httpVary: Accept-Encoding
Flask Implementation: Brotli + Gzip + Fallback
Install Dependencies
bashpip install brotli
Basic Flask Compression Middleware
pythonfrom 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:
pythonresponse.headers["Content-Encoding"] = "identity"
Static Files: Best Practice
For production:
-
Precompress assets:
file.js.brfile.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:
- Brotli for modern browsers
- Gzip fallback
- Plain response as last resort
-
Always set
Vary: Accept-Encoding - 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:




