Why Postman Works But Browsers Block Your API (CORS)
π― The Questionβ
"You build a backend REST API. When testing endpoints in Postman, curl, or your terminal, every request succeeds with
200 OK. But the moment your frontend React app calls the exact same URL, the browser blocks the response with a red CORS error! Why doesn't Postman enforce CORS, and why do browsers?"
β‘ 30-Second Elevator Pitchβ
The fundamental revelation is that CORS is a Browser-Enforced Security Mechanism, not a backend firewall:
- Postman and curl are standalone developer tools. They do not store a user's logged-in banking session cookies or authenticate arbitrary third-party website scripts.
- Web browsers execute untrusted JavaScript downloaded from arbitrary websites across the internet.
Without the browser's Same-Origin Policy (SOP):
- You visit a malicious website:
evil-attacker.com. - That page runs a hidden background script:
fetch('https://yourbank.com/api/transfer-funds'). - Because you are logged into your bank, the browser would automatically attach your authenticated banking cookies, transferring your money without your consent!
To stop this, browsers enforce CORS: if your frontend (localhost:3000) tries to call an API on another domain (api.myapp.com), the browser blocks the response unless the backend explicitly sends the header:
Access-Control-Allow-Origin: http://localhost:3000
π§ Under-the-Hood: The Browser Preflight (OPTIONS) Handshakeβ
π¬ What Defines an "Origin"?β
Two URLs share the Same Origin if and only if all three match:
URL Comparison against https://api.domain.com:443 | Same Origin? | Reason |
|---|---|---|
https://api.domain.com/users | β Yes | Protocol, host, and default 443 port match |
http://api.domain.com/users | β No | Protocol mismatch (http vs https) |
https://domain.com/users | β No | Subdomain mismatch (api.domain.com vs domain.com) |
https://api.domain.com:8080/users | β No | Port mismatch (8080 vs 443) |
π Comparison Matrix: Postman / cURL vs. Web Browsersβ
| Dimension | Postman / cURL CLI | Web Browser |
|---|---|---|
| Environment | Controlled developer environment | Untrusted sandbox executing arbitrary web JS |
| Same-Origin Policy | β None | β Strictly enforced |
| Auto-Attach Cookies | No (Unless manually provided) | β Automatically attaches ambient session cookies |
| Preflight (OPTIONS) | Never sends preflight | Sends preflight on non-simple requests |
| Who Blocks the Request? | Nobody | The Browser client-side engine blocks the response |
π‘ What Interviewers Ask Next (Follow-Up Traps)β
-
"Does a CORS error mean the request never reached the server?"
- Answer: No! This is a massive trap. For simple requests (like standard
GETrequests), the browser actually sends the request and the server executes it! The server computes the database query and responds with data. When the response reaches the browser, the browser inspects the missingAccess-Control-Allow-Originheader and hides the response from JavaScript. The write operation already succeeded on the backend!
- Answer: No! This is a massive trap. For simple requests (like standard
-
"Why is
Access-Control-Allow-Origin: *dangerous with authenticated APIs?"- Answer: Using wildcard
*allows any website on the internet to read API responses. Furthermore, the browser standard strictly forbids wildcard origins whenAccess-Control-Allow-Credentials: trueis enabled. If you send cookies, you must specify the exact, explicit origin URL.
- Answer: Using wildcard
Interview Answer: Postman works because it is a direct HTTP client that does not execute untrusted third-party scripts or attach browser cookies. Browsers enforce the Same-Origin Policy to protect users from malicious sites stealing data or triggering unauthorized actions. The backend must explicitly authorize the frontend domain via Access-Control-Allow-Origin headers.