The Signal
Simon Willison shipped asgi-gzip 0.3 this week after hitting a nasty production bug: SS E (text/event-stream) responses were being incorrectly compressed by the middleware, sil ently breaking streaming output. The fix was already in Starlette — the upstream library asgi-gzip was extracted from — but a stale GitHub Actions scheduled workflow had stopped syncing the fix downstream. One manual workflow run later , both asgi-gzip and datasette-gzip now correctly skip compression on text/event-stream content types.
If you're running any Python ASGI app with gzip middleware and streaming responses, you may have this bug in production right now and not know it.
Builder's Take
SSE is the backbone of streaming L LM output. Every token-by-token response from GPT-4, Claude, or a local Ollama model that you forward to a browser goes through text/event-stream. If your gzip middleware sil ently mangles that stream, your users see broken output or nothing at all — and you see zero errors in your logs.
Here's the cost /leverage math: SSE streaming is free infrastructure. You write one endpoint, the browser reconnects automatically, and you get real -time UX without WebSockets. The moat for solo builders here is reliability — shipping AI products that don't randomly break in production. A misconfigured middleware layer destroys that moat quietly.
The deeper lesson: extracted libraries drift from their upstreams. asgi-gzip had an automated sync mechanism and it still failed silently. If you're pulling in small utility middleware packages, check their upstream relationship and last release date. A package that hasn 't released in 18 months isn't necessarily stable — it might just be forgotten.
Leverage calculation: Fixing this yourself would cost ~2-4 hours of debugging streaming middleware in production. Upgrading a package costs 30 seconds. But you have to know to check. That's why following builders like Willison who ship fixes in public is worth more than reading release notes.
Tools & Stack
The Packages
- asgi-gzip 0.3 — g zip middleware for any ASGI app (FastAPI, Starlette, Datasette). Free, MIT license .
pip install asgi-gzip==0.3 - datasette-gzip 0.2 + — wrapper for Datasette specifically. If you run Datasette in production, upgrade this too.
- Starlette's built-in GZip Middleware — if you're on Starlette directly, this is already patched. Check you're on Starlette ≥0.27.
How to Check Your Stack
# Check what you're running pip show asgi-gzip starlette datasette-gzip # Upgrade pip install --upgrade asgi-gzip datasette-gzipHow to Verify SSE Is Working Post-Upgrade
# Minimal FastAPI SSE endpoint to test from fastapi import FastAPI from fastapi.responses import StreamingResponse from as gi_gzip import GZipMiddleware import asyncio app = FastAPI() app.add_middleware(GZipMiddleware) async def token _stream(): tokens = ["Hello", " world", " from", " streaming ", " LLM"] for token in tokens: yield f"data: {token}\n\n" await asyncio.sleep(0.1) @app.get("/stream") async def stream(): return StreamingResponse(token_stream(), media_type="text/event-stream" )# Test it — you should see raw SSE tokens, not gzip garbage curl -N http://localhost:8000/streamAlternatives If You Want to Skip the Middleware Entirely
- Caddy / nginx — handle gzip at the reverse proxy level, configure to skip
text/event-streamcontent types. Zero Python code risk. - Cloudflare Workers — if you're prox ying through CF, their compression layer handles SSE correctly by default.
- No gzip — for pure SSE/ streaming endpoints, gzip compression provides near-zero benefit anyway. Streaming payloads are small per-chunk. Consider dis abling gzip selectively.
Ship It This Week
Build a streaming L LM proxy with correct SSE handling in under an hour.
If you've been putting off adding real-time streaming to a Python AI app because SSE felt fragile, this week is your excuse to do it right. Here's the concrete build:
- Spin up a FastAPI app with
asgi-gzip 0.3as middleware - Add a
/chat/streamendpoint that proxies to the OpenAI streaming API (or Anthropic, or Ollama locally) - Return
text/event-streamresponses and verify gzip doesn't corrupt them with the curl test above - Deploy to Fly.io or Railway — both support persistent SS E connections on free/hobby tiers
# Full stack in 4 commands pip install fastapi uvicorn asgi- gzip openai # write your streaming endpoint (see above pattern) uv icorn main:app --reload curl -N http://localhost:8000/chat/streamFly.io hobby tier is $0 for apps that sleep. Railway gives you $5/month free credit. Your streaming AI proxy costs nothing to run at low traffic. That's the leverage — fix the infrastructure once , ship the product.