<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammed Oğuz</title>
    <description>The latest articles on DEV Community by Muhammed Oğuz (@muhammedogz).</description>
    <link>https://dev.to/muhammedogz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F649933%2F00538ff8-17e8-479c-8263-51cb7471ea71.jpeg</url>
      <title>DEV Community: Muhammed Oğuz</title>
      <link>https://dev.to/muhammedogz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muhammedogz"/>
    <language>en</language>
    <item>
      <title>There Is No Network Tab for Server-Side Next.js</title>
      <dc:creator>Muhammed Oğuz</dc:creator>
      <pubDate>Mon, 05 Jan 2026 03:23:17 +0000</pubDate>
      <link>https://dev.to/muhammedogz/there-is-no-network-tab-for-server-side-nextjs-2hpg</link>
      <guid>https://dev.to/muhammedogz/there-is-no-network-tab-for-server-side-nextjs-2hpg</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There is no Network tab for server-side code in Next.js because it runs in Node.js, not in the browser&lt;/li&gt;
&lt;li&gt;The solution is not new or magical; it's about intercepting HTTP at the Node runtime level&lt;/li&gt;
&lt;li&gt;This post shows how to reconnect that missing piece using existing tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/muhammedogz/nextjs-node-http-debug" rel="noopener noreferrer"&gt;github.com/muhammedogz/nextjs-node-http-debug&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Problem
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Server-side HTTP requests are effectively invisible by default.&lt;/p&gt;

&lt;p&gt;When a Server Component fetches data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This runs on the server&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.example.com/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;fetch()&lt;/code&gt; call never touches the browser. It happens entirely within Node.js on your development machine. The browser only receives the rendered HTML.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Developer Experience Gap
&lt;/h2&gt;

&lt;p&gt;If you've built SPAs with React, Vue, or Angular, you have muscle memory for debugging HTTP:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open DevTools&lt;/li&gt;
&lt;li&gt;Click the Network tab&lt;/li&gt;
&lt;li&gt;Watch requests flow in real-time&lt;/li&gt;
&lt;li&gt;Click any request to inspect headers, body, timing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow is immediate and visual. You see exactly what your code is doing.&lt;/p&gt;

&lt;p&gt;Server-side rendering breaks this feedback loop. The HTTP requests still happen, but they happen in a place you can't easily see.&lt;/p&gt;

&lt;p&gt;The workarounds developers reach for feel indirect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Console logging&lt;/strong&gt;: &lt;code&gt;console.log(response)&lt;/code&gt; scattered through the codebase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetch wrappers&lt;/strong&gt;: Custom functions that log before and after every request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability tools&lt;/strong&gt;: Full APM solutions that feel heavyweight for local development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Solution
&lt;/h2&gt;

&lt;p&gt;The solution is intercepting HTTP traffic at the Node runtime level using a debugging proxy like &lt;a href="https://mitmproxy.org/" rel="noopener noreferrer"&gt;mitmproxy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The key insight is that interception must happen &lt;strong&gt;before&lt;/strong&gt; application code runs. You need to patch Node.js itself, not wrap individual HTTP calls in your application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7owc0mw4dj15lzzvdyk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7owc0mw4dj15lzzvdyk4.png" alt="Example Screenshot" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/e49d4d23-0a63-437d-8b65-e8f917556400" rel="noopener noreferrer"&gt;Demo Video&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using mitmproxy
&lt;/h3&gt;

&lt;p&gt;A Node preload script patches both HTTP stacks before your application starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scripts/proxy-preload.cjs&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxyUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HTTP_PROXY&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:8080&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Patch fetch (undici)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setGlobalDispatcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ProxyAgent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undici&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setGlobalDispatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProxyAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;proxyUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c1"&gt;// Patch http/https (axios, got, etc.)&lt;/span&gt;
  &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global-agent/bootstrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The npm script loads this preload script via &lt;code&gt;NODE_OPTIONS&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dev:proxy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_OPTIONS=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;--require ./scripts/proxy-preload.cjs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; HTTP_PROXY=http://127.0.0.1:8080 HTTPS_PROXY=http://127.0.0.1:8080 NODE_TLS_REJECT_UNAUTHORIZED=0 next dev"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This setup is intended for local development only and should never be enabled in production. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run &lt;code&gt;pnpm dev:proxy&lt;/code&gt; and every server-side HTTP request appears in mitmproxy's interface.&lt;/p&gt;

&lt;p&gt;For detailed installation and setup instructions, see the &lt;a href="https://github.com/muhammedogz/nextjs-node-http-debug" rel="noopener noreferrer"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To understand why this solution works, you need to know how Node.js actually sends HTTP requests.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. More Technical Context: Node.js Has Two HTTP Stacks
&lt;/h2&gt;

&lt;p&gt;This is the technical detail that explains both the problem and the solution.&lt;/p&gt;

&lt;p&gt;Node.js doesn't have one HTTP implementation. It has two:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;Used By&lt;/th&gt;
&lt;th&gt;Respects HTTP_PROXY?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;undici&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native &lt;code&gt;fetch()&lt;/code&gt;, Ky&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;http/https&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Axios, got, superagent&lt;/td&gt;
&lt;td&gt;Yes (via env vars)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqh6jv7d4okezb9bspvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqh6jv7d4okezb9bspvl.png" alt="How HTTP requests work in Node.js" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why setting &lt;code&gt;HTTP_PROXY&lt;/code&gt; as an environment variable doesn't work consistently. undici deliberately does not respect &lt;code&gt;HTTP_PROXY&lt;/code&gt; by default—this was a security decision by the Node.js team.&lt;/p&gt;

&lt;p&gt;If you only patch one stack, some requests bypass the proxy. A codebase using both fetch and axios would have gaps in visibility.&lt;/p&gt;

&lt;p&gt;The solution patches both stacks, which is why it works consistently across different HTTP libraries.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This is not a new technique or a Next.js workaround.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The core idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js processes can route all HTTP traffic through a proxy&lt;/li&gt;
&lt;li&gt;A preload script ensures patching happens before any application code runs&lt;/li&gt;
&lt;li&gt;Both HTTP stacks (undici and http/https) must be patched for complete coverage&lt;/li&gt;
&lt;li&gt;The result is a Network tab equivalent for server-side code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
