TL;DR
- There is no Network tab for server-side code in Next.js because it runs in Node.js, not in the browser
- The solution is not new or magical; it's about intercepting HTTP at the Node runtime level
- This post shows how to reconnect that missing piece using existing tools
Repository: github.com/muhammedogz/nextjs-node-http-debug
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 don't run in the browser. They run in Node.js.
Server-side HTTP requests are effectively invisible by default.
When a Server Component fetches data:
// This runs on the server
async 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, 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.
3. The Solution
The solution is intercepting HTTP traffic at the Node runtime level using a debugging proxy like 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 in your application.
Using mitmproxy
A Node preload script patches both HTTP stacks before your application starts:
// scripts/proxy-preload.cjs
if (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 script via 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 for local development only and should never be enabled in production.
Run pnpm dev:proxy and every server-side HTTP request appears in mitmproxy's interface.
For detailed installation and setup instructions, see the repository.
To understand why this solution works, you need to know how Node.js actually sends HTTP requests.
4. More Technical Context: Node.js Has Two HTTP Stacks
This is the technical detail that explains both the problem and the solution.
Node.js doesn't have one HTTP implementation. It has two:
| Stack | Used By | Respects HTTP_PROXY? |
|---|---|---|
| undici | Native fetch(), Ky |
No |
| http/https | Axios, got, superagent | Yes (via env vars) |
This is why setting HTTP_PROXY as an environment variable doesn't work consistently. undici deliberately does not respect HTTP_PROXY by default—this was a security decision by the Node.js team.
If you only patch one stack, some requests bypass the proxy. A codebase using both fetch and axios would have gaps in visibility.
The solution patches both stacks, which is why it works consistently across different 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. The concept of 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 any application code runs
- Both HTTP stacks (undici and http/https) must be patched for complete coverage
- 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.


Top comments (0)