Skip to content
Back to Build Tutorials

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 response

Common 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.