Workers SDK Issue Reports

← Back to Dashboard

#10375 Miniflare corrupts response when returning a compressed subrequest

Recommendation:KEEP OPEN
Difficulty:Confirmed bug: undici decompresses responses but Content-Length header is preserved, causing browser truncation. No fix merged yet.
Reasoning:

Fix Content-Length header deletion in dispatchFetch when body is decompressed

Suggested Action:

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.ts lines 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:

  1. Browser receives a response with Content-Length: 692 (compressed size)
  2. The actual body is now decompressed (e.g., 2KB of JavaScript)
  3. Browser reads only 692 bytes and considers the response complete
  4. 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:

  1. Setting up a Worker that proxies requests to an origin that returns compressed responses
  2. Using wrangler dev or any Miniflare-based local development
  3. Making a request for a resource that the origin compresses (e.g., JavaScript file)
  4. 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

  1. packages/miniflare/src/index.ts - Add Content-Length header deletion when body is decompressed

Testing Recommendations

  1. 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-Length header (or has the correct decompressed length)
    • Verifies body content is complete and not truncated
  2. Integration Test:

    • Create a Worker that proxies to a real compressed endpoint
    • Verify the full response body is returned without truncation
  3. 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.

Add Note