Network Debug
Debug HTTP/CORS/WebSocket issues by reading the actual request and response, not the code that generates them.
Why
Network bugs live at boundaries — your fetch calls a service that calls another service — and the code on each side looks correct in isolation. The bug is in what actually crosses the wire: a header you didn't send, a CORS preflight you didn't expect, a 301 that turned your POST into a GET, a JSON body that's actually text/plain. Reading the code tells you what you intended; reading the Network tab tells you what happened. The Network tab (or mitmproxy / Charles for non-browser) is the single most underused debugging tool — 30 seconds there saves an hour of reading fetch wrappers. The method is: reproduce the call, inspect request + response headers + body, compare to what the server expects, fix the mismatch.
When
When a request fails, returns wrong data, or behaves differently than the code suggests. Takes 15-30 min. Skip never for integration code.
How
1. Reproduce the failing request and open the Network tab (or mitmproxy) - Chrome DevTools → Network → filter by Fetch/XHR → find the red row - For non-browser (server-to-server): mitmproxy -p 8080, point your code at it 2. Inspect the actual request: method, URL, headers, body - Compare to what you intended: is the Content-Type actually application/json? Is the Auth header there? - Common surprise: a 301 redirected POST → GET, dropping your body 3. Inspect the actual response: status, headers, body - A 200 with an error in the body is still a failure; a 401 with a clear message is a clue - For CORS: the browser console shows the exact missing header — read it, don't guess 4. Compare actual vs expected and find the mismatch - Server expects Authorization: Bearer <token>, client sent authorization: <token> (no Bearer prefix) - The mismatch is the bug; the code that generated the request is the fix site 5. Fix and verify with the Network tab still open - Don't trust the code; trust the wire — re-inspect to confirm the request now matches
Example
# Browser: Chrome DevTools → Network tab
# 1. Reproduce the failing call
# 2. Click the red row → Headers sub-tab
# Request Headers:
# Content-Type: text/plain;charset=UTF-8 ← BUG: should be application/json
# (no Authorization header) ← BUG: forgot to send the token
# Response Headers:
# Access-Control-Allow-Origin: null ← CORS will block this
# 3. Payload sub-tab → see the actual body sent
# Server-to-server: mitmproxy intercepts the call
mitmproxy -p 8080
# then in your code: process.env.HTTP_PROXY = "http://localhost:8080"
# mitmproxy shows every request/response in real time
# Common fix — the fetch wrapper dropped the Content-Type
// BUG: fetch(url, { body: JSON.stringify(data) }) ← no headers
// FIX:
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
body: JSON.stringify(data),
});
# CORS — read the browser console, it tells you exactly what's missing
# "Access to fetch at 'https://api.x' from origin 'https://app.x' has been blocked
# by CORS policy: No 'Access-Control-Allow-Origin' header is present"
# Fix on the SERVER (not the client): add the header in the API responseCommon Problems
- ⚠Reading the fetch wrapper code instead of the Network tab — the wrapper looks right but a middleware rewrites the header.
- ⚠Blaming CORS on the client and trying to fix it there — CORS is a server response header, always.
- ⚠Not noticing a 301 redirect silently converting POST to GET and dropping the body — the request 'works' but does the wrong thing.
- ⚠Inspecting only the response body and not the headers — the bug is often a missing Set-Cookie or wrong Content-Type.
Best Practices
- ✓Keep mitmproxy or DevTools open during any integration work — the wire is the source of truth, not the code.
- ✓For CORS, always fix the server (add the Allow-Origin header) and never the client — the browser is enforcing a server policy.
- ✓Log the full request (method, URL, headers, body) at debug level on the server so a user report includes the exact call that failed.
- ✓Add an integration test that asserts the actual wire-level request (use nock or msw) so a header regression fails CI, not prod.