CORS errors in single-page applications usually appear when the browser blocks a frontend request because the API server has not allowed that specific origin, method, header, or credential mode. The confusing part is that the request may work in Postman, curl, or a backend job, while failing only inside Chrome, Firefox, Safari, or Edge.
That happens because CORS is enforced by browsers as part of the web security model. A SPA built with React, Vue, Angular, Svelte, Next.js, Nuxt, or another frontend framework can trigger CORS problems when it calls APIs hosted on another domain, subdomain, port, or protocol.
The practical challenge is that complex SPAs often use several environments at once: local development, staging, production, authentication providers, file storage, CDNs, serverless functions, and third-party APIs. A small mismatch between these layers can create an error that looks vague in the console.
This guide explains how to diagnose the problem, how to read the browser error correctly, which server headers matter, what mistakes to avoid, and when the issue should be handled by the backend, DevOps, or security team.
Important note: do not solve CORS errors by allowing every origin with sensitive APIs. CORS is not a replacement for authentication, authorization, CSRF protection, or secure session handling. For applications that handle payments, private accounts, health data, or business records, review the configuration carefully before publishing changes.
Why CORS Errors Happen in Single-Page Applications
A single-page application runs in the user’s browser. When it sends a request to a different origin, the browser checks whether the target server explicitly allows that request. An origin is defined by the protocol, domain, and port. For example, https://app.example.com and https://api.example.com are different origins.
In practice, this means the frontend cannot fully “fix” CORS by itself. The API response must include the right CORS headers. A frontend developer can adjust the request, remove unnecessary custom headers, or use a development proxy, but the final permission comes from the server.
A common mistake is assuming CORS means the API is down. Often, the API responds correctly, but the browser hides the response from JavaScript because the CORS policy failed. That is why the Network tab is usually more useful than the Console tab.
| Symptom | Possible Cause | What to Check First |
|---|---|---|
| Works in Postman but fails in browser | Browser is enforcing CORS | Check response headers in DevTools |
| Fails only on production | Production domain is not allowlisted | Compare staging and production CORS settings |
| Fails after adding Authorization header | Preflight request is not allowed | Check OPTIONS response and allowed headers |
| Fails when using cookies | Credentials are not configured correctly | Check credentials mode, cookies, and allowed origin |
How to Read a CORS Error Without Guessing
The browser console usually gives a short message, but the Network tab gives the real clues. When a request fails because of CORS, inspect both the actual request and the possible preflight request. A preflight request uses the OPTIONS method before the real request.
Preflight usually appears when the browser needs to confirm whether the server accepts a non-simple request. This can happen when the request uses methods like PUT, PATCH, or DELETE, sends credentials, or includes headers such as Authorization or custom application headers.
When troubleshooting, avoid changing several things at once. In many real projects, the fastest path is to reproduce the issue with the smallest possible request, then add headers, credentials, and request body settings one by one.
- Open the browser DevTools and inspect the Network tab.
- Confirm the exact frontend origin shown in the request.
- Check whether an OPTIONS request appears before the real request.
- Inspect the response headers returned by the API server.
- Compare the failing request with a request that works in the same app.
- Verify whether the problem happens only in one browser, environment, or route.
A Practical Workflow for Resolving CORS Errors
Resolving CORS errors is easier when you follow a fixed order. Random changes can hide the real cause, especially in SPAs with multiple API clients, interceptors, authentication flows, and deployment environments.
-
Identify the exact origin.
Check the protocol, domain, and port of the SPA.
http://localhost:3000andhttp://localhost:5173are different origins, even if both run on your machine. Add only the origins that should be allowed. -
Confirm the target API URL.
Make sure the frontend is calling the expected backend. Environment variables can point to an old staging URL, a CDN URL, or a serverless function with a separate CORS configuration.
-
Inspect the preflight response.
If the browser sends an OPTIONS request, the server must answer it correctly. The response should allow the requested method and headers. If OPTIONS returns 404, 405, 500, or redirects, the real request may never happen.
-
Check allowed methods and headers.
If your request sends
Authorization,Content-Type, or custom headers, the server may need to include them inAccess-Control-Allow-Headers. If the method isPATCHorDELETE, it must be allowed too. -
Review credentials carefully.
If the SPA uses cookies or HTTP authentication, the server must allow credentials and the frontend request must enable them. Avoid using a wildcard origin with credentials, because browsers do not accept that combination for credentialed requests.
-
Test one environment at a time.
Local, staging, and production often have different domains and proxies. Fix the failing environment directly instead of assuming the same headers are active everywhere.
-
Validate with a real browser.
Postman and curl can confirm that the API responds, but they do not enforce CORS like browsers do. Always finish the test in the browser where the user will access the SPA.
Important CORS Headers and What They Mean
CORS is controlled through HTTP headers. The backend, gateway, CDN, or serverless layer sends these headers to tell the browser which cross-origin requests are allowed. In complex systems, more than one layer may modify headers, so the final response is what matters.
The most important header is Access-Control-Allow-Origin. It should match the requesting origin or use a safe allowlist strategy. For public resources without credentials, a wildcard may be acceptable. For private APIs, account dashboards, or authenticated sessions, a strict origin policy is safer.
| Header | Purpose | Common Mistake |
|---|---|---|
Access-Control-Allow-Origin |
Defines which origin may read the response | Using * for sensitive authenticated APIs |
Access-Control-Allow-Methods |
Defines which HTTP methods are allowed | Forgetting PATCH, PUT, or DELETE |
Access-Control-Allow-Headers |
Defines which request headers may be used | Forgetting Authorization or custom headers |
Access-Control-Allow-Credentials |
Allows credentialed browser requests when configured correctly | Combining credentials with a wildcard origin |
Access-Control-Max-Age |
Lets the browser cache preflight permission for a limited time | Setting long caching before validating the policy |
Frontend Settings That Can Trigger CORS Problems
Although the server controls CORS permission, the frontend request can still trigger the problem. Adding one header may convert a simple request into a preflighted request. Changing from token-based auth to cookie-based auth can also change the required configuration.
For example, a SPA using fetch with credentials: "include" has different requirements from a simple public GET request. Axios has similar behavior when withCredentials is enabled. These settings are useful, but they must match the backend policy.
In many cases, the frontend should avoid sending unnecessary headers. Some teams add headers globally through interceptors, such as X-Requested-With or custom tracking headers, and later wonder why basic requests started failing. Keep global request configuration clean.
- Remove custom headers that the API does not actually need.
- Use
credentialsorwithCredentialsonly when cookies or browser credentials are required. - Confirm that the frontend environment variable points to the correct API.
- Avoid mixing HTTP and HTTPS between the SPA and API.
- Check whether an interceptor is modifying requests globally.
- Test the same endpoint with and without authentication headers.
Backend and Infrastructure Fixes That Usually Work
The right backend fix depends on where the response is generated. In a traditional API, CORS may be configured in the application framework. In a serverless setup, it may be configured in the function response, API gateway, or hosting platform. Behind a CDN, response headers may also be changed at the edge.
The safest approach is to build an explicit allowlist. Instead of allowing every origin, compare the incoming Origin header with a list of trusted frontend origins. If it matches, return that origin in Access-Control-Allow-Origin. If it does not match, do not grant browser access.
For APIs that use cookies, also review cookie attributes such as SameSite, Secure, and domain scope. A request can look like a CORS issue when the real problem is that the cookie is not being sent or accepted by the browser.
Example policy logic
A practical production policy may allow https://app.example.com and https://admin.example.com, while rejecting random origins. For local development, it may allow http://localhost:3000 and http://localhost:5173, but only outside production.
Common Mistakes That Make CORS Worse
The fastest-looking fix is often the riskiest one. Setting Access-Control-Allow-Origin: * everywhere may make a public test pass, but it can create bad habits and expose private API behavior in ways the team did not intend.
Another common mistake is trying to bypass CORS in the browser with extensions or disabled security flags. That may help during a quick local test, but it does not fix the application for real users. It can also hide the issue until deployment.
Also be careful with redirects. If the preflight request receives a redirect to a login page, another domain, or a trailing-slash URL, the browser may block the request. The OPTIONS route should normally respond directly with the required permission headers.
| Mistake | Why It Causes Trouble | Better Approach |
|---|---|---|
| Allowing all origins on private APIs | It weakens the browser-side boundary for sensitive endpoints | Use a trusted origin allowlist |
| Fixing only the actual request | The browser may block the request at the preflight stage | Handle OPTIONS correctly |
| Adding headers globally in the SPA | It may trigger unnecessary preflight requests | Send only required headers per request |
| Testing only with Postman | Postman does not enforce browser CORS rules | Validate in real browsers |
When to Use a Proxy, Gateway, or BFF Pattern
In development, a local proxy can make the frontend call appear same-origin. This is useful for cleaner local work, but it should not be confused with a production security solution. The production API still needs a correct cross-origin policy if the browser calls it directly.
For larger SPAs, a backend-for-frontend pattern can reduce CORS complexity. The SPA calls its own backend on the same origin, and that backend communicates with internal services. This can simplify authentication, token handling, logging, and rate limiting.
A gateway can also centralize CORS rules, but only if the team clearly owns the policy. If the gateway, API service, and CDN all set different CORS headers, debugging becomes harder. Choose one responsible layer whenever possible.
When to Ask for Backend, DevOps, or Security Support
You should ask for backend or DevOps support when the browser shows that the API response does not include the required headers, when OPTIONS is not handled, or when the issue appears only after passing through a CDN, load balancer, gateway, or serverless platform.
Security support is recommended when the API handles private user data, payment flows, admin dashboards, authentication cookies, or partner integrations. CORS decisions can affect how browsers expose responses to frontend code, so sensitive systems deserve a careful review.
Professional help is also useful when the team is unsure whether to use cookies, bearer tokens, a BFF layer, or a public API model. The best answer depends on the risk level, browser behavior, deployment architecture, and compliance needs.
Conclusion
CORS errors in single-page applications are best solved by understanding the browser’s security check, inspecting the real response headers, and fixing the server-side policy instead of guessing from the console message alone.
The most reliable path is to identify the exact origin, confirm whether a preflight request is involved, allow only the methods and headers the app truly needs, and keep credentialed requests under a strict origin policy.
If the application handles sensitive data, authentication, payments, or complex infrastructure, involve backend, DevOps, or security support before applying broad CORS rules. A quick fix may silence the browser error, but a careful configuration protects the application for real users.
FAQ
1. Why does my API work in Postman but fail in the browser?
Postman does not enforce CORS the same way browsers do. CORS is a browser security mechanism, so a request can succeed in Postman while still being blocked by Chrome, Firefox, Safari, or Edge. This usually means the API is reachable, but the server response does not give the browser permission to expose that response to frontend JavaScript. Check the Network tab and inspect the response headers returned to the browser.
2. Can I fix CORS only in React, Vue, Angular, or another frontend framework?
Not completely. A frontend framework can change how the request is sent, remove unnecessary headers, or use a development proxy, but the server must return the correct CORS headers. If the API does not allow the frontend origin, the browser will block access to the response. In production, the real fix usually belongs in the API, gateway, serverless function, CDN, or backend configuration.
3. What is a preflight request?
A preflight request is an OPTIONS request sent by the browser before the actual request. It checks whether the server allows the requested method, headers, and origin. Preflight often appears when using methods like PUT, PATCH, or DELETE, or when sending headers such as Authorization. If the OPTIONS response fails or lacks the required headers, the browser may block the real request before it is sent.
4. Why does adding an Authorization header create a CORS error?
The Authorization header can make the browser send a preflight request. The server then needs to allow that header through Access-Control-Allow-Headers. If the backend accepts the final authenticated request but does not handle OPTIONS correctly, the browser blocks the flow first. This is common when authentication is added after the API was originally tested with simple public GET requests.
5. Is it safe to use Access-Control-Allow-Origin: *?
It depends on the type of resource. A wildcard may be acceptable for public, non-sensitive resources that do not use credentials. It is not a good default for private APIs, authenticated dashboards, account data, or payment flows. For sensitive applications, use a specific allowlist of trusted origins. Also remember that wildcard origins do not work with credentialed browser requests in the usual secure configuration.
6. Why does CORS fail only in production?
Production often uses a different domain, HTTPS setup, CDN, gateway, or environment variable from local development. The API may allow localhost but not the real production domain. Another common cause is that staging and production use different infrastructure rules. Compare the exact origin, target URL, response headers, and preflight behavior in each environment before changing the code.
7. Can browser extensions solve CORS errors?
Browser extensions can sometimes bypass CORS locally, but they do not solve the application problem. Real users will not have the same extension, and production browsers will still enforce the policy. Extensions can be useful only for temporary debugging when you clearly understand the risk. The correct fix is to configure the server, gateway, or development proxy properly.
8. Why do cookies make CORS more complicated?
Cookies involve credentialed requests, which require stricter configuration. The frontend must send the request with the correct credentials option, and the server must explicitly allow credentials. The allowed origin must also be specific, not a wildcard. Cookie attributes such as SameSite, Secure, and domain scope can also affect whether the cookie is sent or accepted by the browser.
9. What should I check when OPTIONS returns 404 or 405?
If OPTIONS returns 404 or 405, the server probably does not handle preflight requests for that route. The browser may block the actual request even if the real endpoint works with GET, POST, or another method. Configure the API, gateway, or serverless function to respond to OPTIONS with the proper CORS headers. Also check that authentication middleware is not blocking OPTIONS before CORS headers are added.
10. Can a CDN cause CORS problems?
Yes. A CDN can cache, remove, override, or add response headers. If the origin server sends correct CORS headers but the browser receives different headers, inspect the final response after the CDN. Cache rules can also cause one origin’s response headers to be served incorrectly to another origin. For complex deployments, decide whether CORS is owned by the API, CDN, or gateway, and keep the rule consistent.
11. What is the best way to handle CORS in local development?
For local development, you can allow specific localhost origins or use the development server’s proxy feature. A proxy can reduce noise while building the SPA, especially when the frontend and backend run on different ports. However, do not assume that a local proxy represents production behavior. Before deployment, test the real production origin against the real API URL in a browser.
12. When should a team use a backend-for-frontend instead of direct API calls?
A backend-for-frontend can make sense when the SPA depends on several internal services, sensitive tokens, cookie-based sessions, or complex authorization rules. Instead of exposing many cross-origin calls to the browser, the SPA calls its own backend, and that backend communicates with the required services. This can simplify CORS, improve control over authentication, and centralize logging, but it also adds another backend component to maintain.
Editorial note: This article is for educational purposes and does not replace a professional security audit for applications that handle payments, private accounts, regulated data, or sensitive user information.
Official References
- MDN Web Docs — Cross-Origin Resource Sharing (CORS)
- MDN Web Docs — Preflight request
- web.dev — Cross-Origin Resource Sharing
- OWASP Web Security Testing Guide — Testing Cross Origin Resource Sharing





