#10375 Miniflare corrupts response when returning a compressed subrequest
Fix Content-Length header deletion in dispatchFetch when body is decompressed
N/A
Analysis Report
Issue Review: cloudflare/workers-sdk#10375
Summary
Miniflare decompresses compressed subrequest responses but preserves the original Content-Length header, causing browser response truncation and corruption.
Findings
- Created: 2025-08-14
- Updated: 2025-08-14
- Version: wrangler 4.29.0 -> 4.60.0 (current), miniflare 4.20260120.0 (current)
- Component: Miniflare
- Labels: bug
- Comments: 0
Key Evidence
- Related issue #8004 ("miniflare aggressively buffers responses") is still OPEN
- Related issue #9522 ("Miniflare doesn't support zstd compressed subrequests") is still OPEN
- PR #9625 (fix for streaming compression) is still OPEN (not merged)
- No changelog entries reference this issue or related fixes
- The problematic code is still present in the codebase at
packages/miniflare/src/index.tslines 2438-2445
Root Cause Analysis
The bug occurs in the dispatchFetch method in packages/miniflare/src/index.ts (lines 2438-2445):
// At this point, undici.fetch (used inside fetch, above)
// has decompressed the response body but retained the Content-Encoding header.
// This can cause problems for client implementations which rely
// on the Content-Encoding header rather than trying to infer it from the body.
// Technically, at this point, this a malformed response so let's remove the header
// Retain it as MF-Content-Encoding so we can tell the body was actually compressed.
const contentEncoding = response.headers.get("Content-Encoding");
if (contentEncoding)
response.headers.set("MF-Content-Encoding", contentEncoding);
response.headers.delete("Content-Encoding");
The Problem: When undici.fetch() makes a subrequest to an external origin that returns a compressed response (e.g., Content-Encoding: gzip), undici automatically decompresses the body. The code correctly removes the Content-Encoding header and moves it to MF-Content-Encoding, but it fails to delete the original Content-Length header.
The Result:
- Browser receives a response with
Content-Length: 692(compressed size) - The actual body is now decompressed (e.g., 2KB of JavaScript)
- Browser reads only 692 bytes and considers the response complete
- JavaScript/content is truncated, causing parsing errors
This is distinct from but related to issue #8004 (response buffering) and affects all compressed subrequests regardless of the buffering issue.
Reproduction Path
The issue can be reproduced by:
- Setting up a Worker that proxies requests to an origin that returns compressed responses
- Using
wrangler devor any Miniflare-based local development - Making a request for a resource that the origin compresses (e.g., JavaScript file)
- Observing truncated responses due to Content-Length mismatch
Recommendation
Status: KEEP OPEN
Reasoning: This is a confirmed bug with clear root cause. The code explicitly handles Content-Encoding header removal after undici decompression but neglects to remove/update the now-incorrect Content-Length header. No fix has been merged.
Action: Fix the Content-Length header handling in the dispatchFetch method.
Proposed Solution
Code Fix
In packages/miniflare/src/index.ts, around line 2438, after deleting the Content-Encoding header, also delete the Content-Length header:
// At this point, undici.fetch (used inside fetch, above)
// has decompressed the response body but retained the Content-Encoding header.
// This can cause problems for client implementations which rely
// on the Content-Encoding header rather than trying to infer it from the body.
// Technically, at this point, this a malformed response so let's remove the header
// Retain it as MF-Content-Encoding so we can tell the body was actually compressed.
const contentEncoding = response.headers.get("Content-Encoding");
if (contentEncoding) {
response.headers.set("MF-Content-Encoding", contentEncoding);
// Also delete Content-Length since the body has been decompressed
// and the original Content-Length refers to the compressed size
response.headers.delete("Content-Length");
}
response.headers.delete("Content-Encoding");
Files to Modify
packages/miniflare/src/index.ts- AddContent-Lengthheader deletion when body is decompressed
Testing Recommendations
Unit Test: Create a test that:
- Mocks an external origin returning a gzip-compressed response with explicit
Content-Length - Calls
dispatchFetch() - Verifies the returned response has no
Content-Lengthheader (or has the correct decompressed length) - Verifies body content is complete and not truncated
- Mocks an external origin returning a gzip-compressed response with explicit
Integration Test:
- Create a Worker that proxies to a real compressed endpoint
- Verify the full response body is returned without truncation
E2E Test:
- Use the PostHog reverse proxy scenario described in the issue
- Verify JavaScript files load completely without parsing errors
Implementation Difficulty
Easy
Justification:
- Single line code change in a well-understood location
- The fix follows the same pattern already used elsewhere in the codebase (line 1385-1387 shows
delete headers["content-length"]when encoding is applied) - Clear cause-and-effect relationship
- Low risk of regression since we're removing a header that's already incorrect for decompressed content
Notes & Feedback (0)
No notes yet.