When Postman Says Yes but Browser Says No
Mid-project, I hit this wall. The backend API worked perfectly in Postman. Clean 200 OK, data exactly as expected. But when I called it with fetch in the actual web app? Failure. Console showed CORS error, data came back as undefined.
At first, I blamed my code. Re-checked the fetch syntax, shuffled async/await order, added try-catch blocks. Same error. Asked the backend team—got back "Works fine in Postman though."
Finally, I opened the Network tab in browser DevTools to see what was actually happening. And there it was. The request went out fine, but the server's response headers were missing Access-Control-Allow-Origin. Postman doesn't care about CORS because it's not a browser. The problem wasn't my code—it was server configuration.
That's when it clicked. The Network tab is the black box that shows truth between code and server. When your code claims "I sent the request" and the server claims "I responded fine," the Network tab shows you what actually happened, no lies.
Reading the Network Tab Like a Detective
Open the Network tab and you'll see every HTTP request listed chronologically. Click on any request and detailed info appears on the right. Here are the tabs that matter.
Headers: The Passport of Requests and Responses
The Headers tab is like a passport for your request and response. It shows where it came from, what credentials it has, what it's asking for.
In Request Headers, check:
Authorization: Is the token actually attached?Content-Type: Is itapplication/jsonorapplication/x-www-form-urlencoded?Origin: The browser auto-attaches this (used for CORS decisions)
In Response Headers, check:
Access-Control-Allow-Origin: Is CORS allowed?Content-Type: What format did the server send? (If you expected JSON but got HTML, it's probably an error page)Set-Cookie: Are cookies being set properly?
// Example: Sending request with Authorization header
fetch('/api/user/profile', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
})
Click on this request in the Network tab and you'll see if Authorization: Bearer eyJhbGc... actually got sent. I've seen cases where the code clearly sets the token but null gets transmitted. Usually a variable scope or timing issue.
Preview and Response: Checking the Actual Data
Preview formats JSON nicely, shows it as a tree structure. Great for inspecting deeply nested data.
Response shows the raw output. If the server sent an HTML error page instead of JSON, you'll catch it here.
Once, an API returned { "data": null } and I couldn't figure out why. Looked at the Response tab—turns out it was { "data": null, "error": "Invalid token" }. My code was only looking at response.data, so I missed the error message.
Timing: Finding the Bottleneck
The Timing tab breaks down the request lifecycle into milliseconds. Like a slow-motion replay of a race.
- Queueing: Time spent waiting in browser's queue (due to concurrent connection limits)
- Stalled: Waiting before actual transmission
- DNS Lookup: Time to convert domain to IP
- Initial Connection: TCP handshake time
- SSL: HTTPS certificate negotiation
- Request Sent: Time to send the request (usually very short)
- Waiting (TTFB): Time until server sends first byte (server processing time)
- Content Download: Time to receive response data
When a page feels slow, Timing tells you why. If TTFB is 5 seconds, the server's slow. If Content Download is 5 seconds, it's network or data size.
// Example: Timing a slow API call
async function loadUserData() {
const start = performance.now();
const response = await fetch('/api/users/list');
const data = await response.json();
const end = performance.now();
console.log(`Total time: ${end - start}ms`);
// Compare with Network tab Timing to see where the delay is
}
Status Codes in Context
Status codes are the server's facial expressions. Even within the 400s, meanings differ.
- 200 OK: Success. But still check the actual data (some servers send errors with 200)
- 201 Created: Resource created successfully (you'll get this after POST to create a user)
- 204 No Content: Success but no response body (common after DELETE)
- 301 Moved Permanently: Permanent redirect (URL changed)
- 400 Bad Request: Request format is wrong (JSON syntax error, missing required field)
- 401 Unauthorized: Not authenticated (no token or expired token)
- 403 Forbidden: No permission (authenticated but access denied)
- 404 Not Found: Resource doesn't exist (URL typo, deleted data)
- 500 Internal Server Error: Server crashed (check server logs)
Real-world confusion I had: After login, dashboard kept returning 401. Login succeeded, so why 401? Network tab showed the login API returned 200, but the next dashboard API request had no token in headers. The code that saved the token to localStorage after login had an async timing issue and ran too late.
Pro Tips: Filtering, Preserving Logs, Copying as cURL
Filter Out the Noise
Load one web page and dozens of requests fly—images, CSS, JS, fonts, analytics. To see just the API calls you care about, use filters.
- Click the
Fetch/XHRfilter to show only API requests - Type a domain or path in the search box (like
/api/user) to filter specific requests
Preserve Log to Track Page Transitions
When a page redirects or refreshes, the Network tab clears. Turn on the Preserve log checkbox and previous requests stay visible after page transitions.
Essential for debugging login flows with redirects. You can see if the login POST succeeded, then which request failed afterward—the whole sequence.
Copy as cURL to Reproduce
Right-click a request and select Copy > Copy as cURL to get a terminal-executable cURL command. It includes headers, cookies, body—everything needed to reproduce the exact same request.
When reporting bugs to backend teams, send this. Cuts out the "How did you make the request?" back-and-forth. They can run the exact same request and see it themselves.
# Copy as cURL example (actual output)
curl 'https://api.example.com/users/123' \
-H 'Authorization: Bearer eyJhbGc...' \
-H 'Content-Type: application/json' \
--compressed
Throttling to Simulate Slow Networks
At the top of the Network tab, there's a Throttling dropdown. Select options like Fast 3G or Slow 3G to artificially slow down your network.
Test how the UI looks when images load slowly, or how user experience degrades when API responses take 5 seconds. Useful for checking if loading spinners show up, if timeout handling works.
The Network Tab Doesn't Lie
Code can claim "I sent it," the server can claim "I received it." But what actually happened? Only the Network tab knows.
The core of API debugging is observation, not guessing. Not "I think the header wasn't attached," but opening the Headers tab to actually check. Not "The server's probably slow," but looking at Timing to measure TTFB.
The Network tab is like a detective's dashcam. It reconstructs the crime scene accurately. Go through Headers, Preview, Response, Timing tabs in order, and you can trace exactly where and how a request failed—or succeeded but gave unexpected results.
Next time someone says "Works in Postman but not in the browser," open the Network tab first. The answer's in there.