Understanding CORS: Why Your Request Failed
When your JavaScript application throws a CORS error, it’s not broken code—it’s the browser protecting users from potential security threats. Understanding why these errors occur requires grasping the fundamental security model that governs how browsers handle cross-origin requests.
Key Takeaways
- CORS errors occur when browsers block cross-origin requests that lack proper server permissions
- The Same-Origin Policy protects users by restricting requests between different origins
- Simple requests proceed directly while complex requests require preflight OPTIONS checks
- Debugging CORS requires examining browser network tabs and verifying server response headers
The Same-Origin Policy Foundation
Browsers enforce the Same-Origin Policy as their primary security mechanism. An origin consists of three parts: protocol (https://), domain (example.com), and port (:3000). When these match exactly, requests flow freely. When they differ—even slightly—you’re making a cross-origin request.
Consider these origins:
https://app.example.com→https://api.example.com(different subdomain = different origin)http://localhost:3000→http://localhost:3001(different port = different origin)http://example.com→https://example.com(different protocol = different origin)
Without this policy, any website could read your Gmail, access your banking data, or make requests on your behalf. Cross-Origin Resource Sharing (CORS) provides controlled relaxation of these restrictions.
How Browsers Enforce CORS
The browser acts as the enforcer, not the server. This distinction matters because it explains common misconceptions:
- “It works in Postman/curl” - These tools aren’t browsers; they don’t enforce CORS
- “Adding
no-corsmode will fix it” - This doesn’t disable CORS; it creates an opaque response you can’t read - “The server is blocking my request” - The server responds normally; the browser blocks access to the response
When you make a cross-origin request, the browser automatically adds an Origin header. The server must respond with an Access-Control-Allow-Origin header that matches your origin (or * for public resources). No matching header means the browser blocks access to the response—hence your CORS error.
Simple vs. Preflight Requests
Not all requests trigger the same CORS behavior. Simple requests go through immediately, while others require a preflight request.
Simple Requests (No Preflight)
These requests meet ALL these criteria:
- Method:
GET,HEAD, orPOST - Headers: Only simple headers (
Accept,Content-Language,Content-Type) - Content-Type: Only
application/x-www-form-urlencoded,multipart/form-data, ortext/plain
Requests Triggering Preflight
Any request that breaks the simple criteria triggers an OPTIONS preflight:
- Methods like
PUT,DELETE,PATCH - Custom headers (
Authorization,X-API-Key) Content-Type: application/json(yes, JSON triggers preflight)
The preflight asks permission before sending your actual request. The server must respond with appropriate headers:
Access-Control-Allow-Methods(which methods are allowed)Access-Control-Allow-Headers(which headers are permitted)Access-Control-Max-Age(how long to cache this permission)
Discover how at OpenReplay.com.
Common CORS Failure Points
Missing or Incorrect Headers
The server returns no Access-Control-Allow-Origin header, or it doesn’t match your origin. Remember: http://localhost:3000 and http://localhost:3001 are different origins.
Credential Constraints
Including credentials (credentials: 'include' or withCredentials: true) adds restrictions:
- The server must include
Access-Control-Allow-Credentials: true Access-Control-Allow-Origincannot be*—it must specify your exact origin- Cookies must meet SameSite requirements
Redirect Complications
CORS and redirects interact poorly. A redirect to a different origin during a CORS request often fails because the browser doesn’t handle the origin change correctly. Avoid cross-origin redirects in CORS requests.
Private Network Access
Modern browsers add extra protection when public websites access private network resources (localhost, 192.168.x.x, 10.x.x.x). These requests may require additional headers like Access-Control-Allow-Private-Network and trigger preflight even for simple requests. This protection prevents external websites from scanning your local network.
Debugging CORS Effectively
When facing CORS errors:
- Check the browser’s Network tab - Look for the preflight
OPTIONSrequest and examine both request and response headers - Verify the exact error message - “CORS header ‘Access-Control-Allow-Origin’ missing” vs. “CORS header ‘Access-Control-Allow-Origin’ does not match” point to different issues
- Test without credentials first - Remove
credentials: 'include'to isolate credential-related issues - Confirm origin matching - Ensure protocol, domain, and port all match exactly
Conclusion
CORS errors aren’t arbitrary obstacles—they’re the browser protecting users from malicious cross-origin requests. The server declares which origins can access its resources through CORS headers, and the browser enforces these rules. Understanding this model transforms CORS from a mysterious blocker into a predictable system you can debug methodically.
When your request fails, check whether it’s a simple or preflight request, verify the server’s CORS headers match your origin exactly, and remember that tools like Postman bypass these checks entirely. The browser is doing its job—your task is ensuring the server provides the correct permissions.
FAQs
Postman and other API testing tools don't enforce CORS policies. Only browsers implement these security restrictions. Your server needs to send proper Access-Control-Allow-Origin headers for browser requests to succeed.
While browsers offer flags to disable security features, this is risky and affects all sites. Instead, configure your development server to send appropriate CORS headers or use a proxy server to handle cross-origin requests during development.
The CORS specification only considers three content types as simple: application/x-www-form-urlencoded, multipart/form-data, and text/plain. Any other content type, including application/json, triggers a preflight OPTIONS request to verify permissions.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.