Press enter or click to view image in full size
1. The Problem
Browser DevTools only show client-side traffic. When you open the Network tab, you see requests made by JavaScript running in the browser.
But Server Components, Route Handlers, and Server Actions do not run in the browser. They run in Node.js.
As a result, server-side HTTP requests are effectively invisible by default.
When a Server Component fetches data:
// This runs on the serverasync function UserProfile({ userId }: { userId: string }) { const response = await fetch(`https://api.example.com/users/${userId}`); const user = await response.json(); return <div>{user.name}</div>;}
That fetch() call never touches the browser.
It happens entirely within Node.js on your development …
Press enter or click to view image in full size
1. The Problem
Browser DevTools only show client-side traffic. When you open the Network tab, you see requests made by JavaScript running in the browser.
But Server Components, Route Handlers, and Server Actions do not run in the browser. They run in Node.js.
As a result, server-side HTTP requests are effectively invisible by default.
When a Server Component fetches data:
// This runs on the serverasync function UserProfile({ userId }: { userId: string }) { const response = await fetch(`https://api.example.com/users/${userId}`); const user = await response.json(); return <div>{user.name}</div>;}
That fetch() call never touches the browser.
It happens entirely within Node.js on your development machine.
The browser only receives the rendered HTML.
2. The Developer Experience Gap
If you’ve built SPAs with React, Vue, or Angular, you have muscle memory for debugging HTTP:
- Open DevTools
- Click the Network tab
- Watch requests flow in real time
- Click any request to inspect headers, body, and timing
This workflow is immediate and visual. You see exactly what your code is doing.
Server-side rendering breaks this feedback loop. The HTTP requests still happen, but they happen in a place you can’t easily see.
The workarounds developers reach for feel indirect:
- Console logging:
console.log(response)scattered through the codebase - Fetch wrappers: custom functions that log before and after every request
- Observability tools: full APM solutions that feel heavyweight for local development
None of these replicate the immediacy of the Network tab. You’re adding code to see what code is doing, rather than simply observing it.
This feels like a regression, even though it isn’t. Server-side rendering isn’t worse — it’s different. The tooling awareness just hasn’t caught up.
3. The Solution
The solution is intercepting HTTP traffic at the Node.js runtime level using a debugging proxy such as mitmproxy.
The key insight is that interception must happen before application code runs. You need to patch Node.js itself, not wrap individual HTTP calls.
Press enter or click to view image in full size
Proxy Demo
Using mitmproxy
A Node preload script patches both HTTP stacks before your application starts:
// scripts/proxy-preload.cjsif (process.env.NODE_ENV === "development") { const proxyUrl = process.env.HTTP_PROXY || "http://127.0.0.1:8080";
// Patch fetch (undici) const { setGlobalDispatcher, ProxyAgent } = require("undici"); setGlobalDispatcher(new ProxyAgent(proxyUrl)); // Patch http/https (axios, got, etc.) require("global-agent/bootstrap");
The npm script loads this preload file using NODE_OPTIONS:
{ "dev:proxy": "NODE_OPTIONS=\"--require ./scripts/proxy-preload.cjs\" HTTP_PROXY=http://127.0.0.1:8080 HTTPS_PROXY=http://127.0.0.1:8080 NODE_TLS_REJECT_UNAUTHORIZED=0 next dev"}
This setup is intended only for local development and should never be enabled in production.
Run:
pnpm dev:proxy
Every server-side HTTP request will now appear in mitmproxy’s interface.
For full setup instructions, see the repository.
4. More Technical Context: Node.js Has Two HTTP Stacks
This detail explains both the problem and the solution.
Node.js does not have a single HTTP implementation. It has two:
StackUsed byRespects HTTP_PROXYundiciNative fetch(), KyNohttp/httpsAxios, got, superagentYes (via env vars)
Press enter or click to view image in full size
This is why setting HTTP_PROXY alone doesn’t work consistently.
Undici deliberately ignores HTTP_PROXY by default — a security decision by the Node.js team.
If you patch only one stack, some requests bypass the proxy.
A codebase using both fetch and axios will have blind spots.
The solution patches both stacks, which is why it works consistently across HTTP libraries.
5. Final Thoughts
This is not a new technique or a Next.js workaround.
Tools like Proxyman, Charles Proxy, and mitmproxy have used this model for years. Intercepting HTTP at the runtime level is well-established.
What’s missing is awareness, not capability.
The core idea:
- Node.js processes can route all HTTP traffic through a proxy
- A preload script ensures patching happens before application code runs
- Both HTTP stacks must be patched for full visibility
- The result is a Network tab equivalent for server-side code
The goal is regaining a familiar and productive debugging experience. Instead of adding logging code or reaching for heavy observability tools during development, you can simply watch requests flow through a proxy interface.