Brotli ASGI Middleware
brotli-asgi is an ASGI middleware that provides Brotli response compression for ASGI applications like Starlette and FastAPI. It offers faster and more dense compression than GZip, making it a drop-in replacement for the GZipMiddleware. As of v1.6.0, it remains actively maintained with periodic updates for dependencies and occasional feature enhancements.
Warnings
- breaking A security vulnerability (GHSA-3qj8-93xh-pwh2) was addressed in v1.4.0. Users on older versions should upgrade immediately to mitigate potential risks related to underlying Starlette dependencies.
- gotcha Brotli compression can be CPU-intensive at higher quality settings. While it offers better compression ratios, it might increase latency (Time to First Byte) for highly dynamic content. Consider balancing `quality` and `minimum_size` parameters or pre-compressing static assets.
- gotcha The middleware will not apply compression if a `Content-Encoding` header is already set on the response, to prevent double-encoding. Additionally, the correct double-encoding prevention behavior when using `gzip_fallback=True` requires Starlette version >=0.22.0.
- gotcha The order of middleware in ASGI applications is critical. Ensure `BrotliMiddleware` is placed appropriately, especially relative to other middleware that might modify response bodies or headers (e.g., authentication, CORS).
- gotcha To exclude specific routes or handlers from compression, you typically leverage the ASGI framework's (e.g., Starlette, FastAPI) routing capabilities to apply middleware conditionally or to specific sub-applications/routers, rather than relying on a direct exclusion parameter within `BrotliMiddleware` itself.
Install
-
pip install brotli-asgi
Imports
- BrotliMiddleware
from brotli_asgi import BrotliMiddleware
Quickstart
from fastapi import FastAPI
from brotli_asgi import BrotliMiddleware
from starlette.responses import JSONResponse
app = FastAPI()
app.add_middleware(
BrotliMiddleware,
quality=4, # Compression quality (0-11, higher is slower but denser)
mode="text", # Compression mode: "generic", "text", or "font"
minimum_size=400, # Only compress responses larger than this many bytes
gzip_fallback=True # Fallback to gzip if 'br' is not in Accept-Encoding
)
@app.get("/")
async def home():
return JSONResponse({"data": "a" * 4000})
# To run this example, you would typically use uvicorn:
# uvicorn your_module_name:app --reload