Opened 50 minutes ago
Last modified 11 minutes ago
#37319 new Uncategorized
GZipMiddleware should skip responses without body.
| Reported by: | Vladimir Nani | Owned by: | |
|---|---|---|---|
| Component: | Uncategorized | Version: | 6.1 |
| Severity: | Normal | Keywords: | GZipMiddleware |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
GZipMiddleware compresses responses with status 304 that are not allowed to have content body.
Negative effect:
- streaming response with 304 status code and empty body is passed to GZipMiddleware
- Middleware check doesnt apply for streaming response and proceeds with compression
- In situation where connections are pooled, for example with Traefik, the body is not read (according to rfc 9112).
- The empty body compressed is left as a garbage in the sockets buffer.
- When the socket is read for the next time instead of starting with "HTTP" it reads "\x1f\x8b\x08\x08\x00\x00\x00\x00\x00\xffaaaa"
- The next unrelated request that reuses the socket fails with malformed http:
{"level":"debug","error":"net/http: HTTP/1.x transport connection broken: malformed HTTP version \"\\x1f\\x8b\\b\\b\\x00\\x00\\x00\\x00\\x00\\xffaaaaaaaaaaaaaaaa\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00HTTP/1.1\"","time":"2026-09-04T12:33:17+02:00","caller":"github.com/traefik/traefik/v3/pkg/proxy/httputil/proxy.go:117","message":"500 Internal Server Error"}
Proposal:
Check for 204 and 304 and do not compress these responses.
https://www.rfc-editor.org/rfc/rfc9112.html#section-6.3-2.1
Note:
See TracTickets
for help on using tickets.
You are right. The middleware currently handles 204, 304, and short responses under 200 bytes only in the non-streaming case. This is the guard in
GZipMiddleware:if not response.streaming and len(response.content) < 200: return responseFor streaming responses, this guard does not apply, and the middleware compresses them anyway — which it should not do.
I tested this to verify for 204, 304, short response, and long response.
Non-streaming (
test.py):Streaming (
test_streaming.py):In the streaming case, even the 204 and 304 responses with empty content still get
Content-Encoding: gzipand non-empty compressed bytes — gzip-compressing an empty sequence still produces gzip header/footer framing, so it's never truly empty. This matches the bug behavior described in the report.I can provide the test files here if needed.
I would like to work on this issue.