惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
D
Docker
GbyAI
GbyAI
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
雷峰网
雷峰网
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Stack Overflow Blog
Stack Overflow Blog
WordPress大学
WordPress大学
小众软件
小众软件
Engineering at Meta
Engineering at Meta
酷 壳 – CoolShell
酷 壳 – CoolShell
I
InfoQ
B
Blog
H
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
A
About on SuperTechFans
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog

Mintlify Blog

22 UX improvements to the web editor Introducing the Mintlify Help Center Starter Kit Introducing the collaborative editor built for teams and agents Workflows, rebuilt Is your documentation agent-ready? Mintlify raises $45M Series B led by Andreessen Horowitz and Salesforce Ventures 5 things you didn't know you could do in the Mintlify web editor The improved Mintlify CLI Docs on autopilot: From zero to self-maintaining with Mintlify The state of agent traffic in documentation (March 2026) How we built a virtual filesystem for our Assistant We Replaced Our Internal Wiki With a Slack Bot. You Should Too. 8 ways teams use Mintlify to keep docs updated automatically Documentation is your AI interface What three years of watching AI in production taught us Bridging two JSX runtimes: How we solved Astro's React children problem AI agents are shipping faster than anyone can document Knowledge management systems for technical teams Workflows: Automate documentation maintenance Mintlify acquires Helicone to redefine AI knowledge infrastructure Why more product managers are switching to Mintlify Auto-generating documentation sites from GitHub repos Your docs, your frontend, our content engine Take control of your documentation system Almost half your docs traffic is AI, time to understand the agent experience @mintlify for better docs, faster Mintlify for Enterprise Real llms.txt examples from leading tech companies (and what they got right) Mintlify + Claude Opus 4.6: Powering AI-native knowledge management Declaring Clankruptcy: An experiment in agent orchestration
Debugging a mysterious HTTP streaming issue
Nick Khami · 2025-08-01 · via Mintlify Blog

We recently encountered a frustrating issue with HTTP response streaming at Mintlify. Our system uses the AI SDK with the Node stream API to forward streams, and suddenly things stopped working properly. The symptoms were confusing: streaming worked perfectly with cURL and Postman, but failed completely with node-fetch and browser fetch.

Our first hypothesis centered around stream compatibility issues. We suspected the problem might be related to how the AI SDK responds using web streams versus Node streams, particularly around HTTP/2 compliance. Web streams have known issues with HTTP/2, while Node streams are compliant.

The Cloudflare Worker Bandaid

While investigating, we discovered an odd workaround. We set up a Cloudflare worker as middleware between our server and clients:

// Cloudflare Worker that acts as a CORS-enabled streaming proxy
export default {
  async fetch(request, env, ctx) {
    // Handle preflight CORS requests
    // ....

    // Extract request body for non-GET requests
    const body = ['GET', 'HEAD'].includes(request.method) ? null : request.body;

    // Create headers that mimic a real browser request
    // ....

    // Forward the request to the target URL
    const response = await fetch(request.url, {
      method: request.method,
      headers: proxyHeaders,
      body: body,
    });

    // Copy response headers and enable CORS
    const responseHeaders = new Headers(response.headers);
    responseHeaders.set('Access-Control-Allow-Origin', '*');

    // Create streaming response to handle large payloads
    const { readable, writable } = new TransformStream();
    response.body?.pipeTo(writable);

    return new Response(readable, {
      status: response.status,
      statusText: response.statusText,
      headers: responseHeaders,
    });
  },
};

This worker simply received the stream and responded with it. Somehow, this completely fixed the issue, which made no sense to us at the time.

Ruling Out HTTP Versions

We quickly invalidated our HTTP/1 vs HTTP/2 suspicion by using cURL with the --http1.1 flag. Even when making HTTP/1 requests, things streamed properly. Throughout the entire debugging process, cURL consistently worked as expected.

One thing that stuck out was that our egress systems (ALB and Cloudflare) were stripping these headers:

'Transfer-Encoding': 'chunked',
'Connection': 'keep-alive',
'Content-Encoding': 'none',

Since browsers frequently have issues with these headers, and given that Postman and cURL worked while node-fetch and browser fetch didn't, we thought this might be the culprit.

Learning About Dynamic Responses

During this investigation, I learned more about "dynamic" responses and HTTP streaming in general. We explored whether an egress proxy was buffering the entire response to assign a content-length header, but that wasn't happening. It was valuable to develop better intuition around what HTTP streaming actually means.

After about 3 hours of making no progress, only Lucas made any headway. His previous Cloudflare experience gave him the intuition that a worker might fix the issue, which led to our temporary solution.

We implemented the patch fix and went to lunch, but had to deal with another issue: changing the IP to the Cloudflare worker triggered our IP-based rate limiter. After fixing that, we called it a day around 8pm.

Later that night, Lucas had an epiphany. Thinking about why cURL worked but fetch didn't, he consulted Claude about the differences between these tools. Claude walked him through a crucial insight: when Lucas examined the request headers, he noticed that cURL wasn't sending an Accept-Encoding header by default, while browsers always do. This missing header was the red flag that led Claude to explain that "cURL doesn't allow compressed responses without the --compressed flag."

As Claude pointed out, this behavior difference is key:

  • Browser requests include Accept-Encoding: gzip, deflate, br by default
  • cURL requests omit Accept-Encoding unless you use the --compressed flag
  • Without the Accept-Encoding header, servers won't compress the response

Bingo. The problem was compression.

Lucas went into the Cloudflare dashboard, disabled compression, and everything worked perfectly. A simple solution to what had been a very annoying problem causing 10+ second latency on requests.

screenshot of Cloudflare dashboard

This was embarrassingly familiar. Denzell and I had actually dealt with this exact issue before at Trieve, fixing it in PR #2002: remove compression for chat routes. The reason we didn't immediately recognize it was what I call "magic obfuscation" - when you've fixed something before but the context makes it unrecognizable.

In fact, Mayank, another team member at Mintlify who worked with Trieve from the customer side, had discovered a similar streaming issue with this message:

hey folks! were there any changes to the message apis yesterday? streaming doesn't seem to be working as intended. maybe github.com/devflowinc/trieve/pull/1999/files?

The key difference was visibility. Since Trieve is source available, Mayank could look at recent commits and quickly identify the issue. In our current Mintlify case, we had no such visibility into what changed, which made the debugging process exponentially more difficult.

The strangest part is that streaming worked correctly one day and stopped working the next, without any changes on our end. ALB configuration stayed the same, Cloudflare wasn't touched, and none of our code changed. Somehow, Cloudflare had silently enabled compression, and we have no idea why.

This highlights a fundamental issue with Cloudflare's approach to infrastructure management. While their "intelligent" defaults and automatic optimizations can be helpful for simple use cases, they create serious problems for production systems that require predictable behavior. The fact that a critical setting like compression can change without explicit configuration updates makes debugging nearly impossible.

In a properly designed infrastructure-as-code environment, this issue would have been immediately obvious. A git diff would show exactly when compression was enabled, by whom, and why. Instead, we spent hours chasing ghosts because Cloudflare's dashboard-driven configuration model obscures the audit trail of changes.

This isn't just about compression—it's about the broader pattern of "helpful" automation that removes visibility and control from engineering teams. When debugging production issues, the last thing you want is uncertainty about whether your infrastructure configuration matches what you deployed.

  1. Compression breaks HTTP streaming - This is now permanently etched in my brain
  2. cURL vs fetch behavior differences - cURL doesn't allow compressed responses without --compressed flag
  3. Cloudflare workers as debugging tools - Sometimes adding middleware can help isolate problems
  4. Source availability matters - Having access to change logs makes debugging much easier
  5. Team knowledge sharing - Lucas's Cloudflare experience was crucial to finding both the workaround and the real solution

If I'm ever on a team using Cloudflare again, we'll make sure compression is disabled for any HTTP streaming endpoints from day one.