What Is Cross-Origin Resource Sharing (CORS) Policy?
If your browser shows a blocked by CORS policy error, the problem usually is not the API itself. It is the browser refusing to let JavaScript read a response from a different origin unless the server explicitly allows it.
Cross-Origin Resource Sharing (CORS) is the browser security mechanism that makes cross-origin requests possible without breaking the same-origin policy. That matters in real applications where a frontend runs on one domain, an API sits on another, and authentication, analytics, or payment services live somewhere else.
This guide explains what CORS policy is, how the browser enforces it, which headers matter, why preflight requests happen, and how to configure CORS correctly without opening security holes. If you need to troubleshoot access-control-allow-headers * allowed behavior, fix access-control-allow-origin not working, or understand why an ajax access-control-allow-origin request fails in the browser, this is the right place to start.
CORS does not grant access by itself. It tells the browser whether the page’s JavaScript is allowed to read the response.
For official browser and platform guidance, see MDN Web Docs, WHATWG Fetch Standard, and OWASP for security context.
Understanding the Same-Origin Policy
The same-origin policy is the default browser rule that limits how documents or scripts from one origin can interact with resources from another. An origin is made up of three parts: scheme or protocol, host or domain, and port. All three must match for two URLs to be considered the same origin.
For example, https://app.example.com and https://app.example.com:443 are effectively the same origin in most cases because the default HTTPS port is implied. But https://app.example.com and https://api.example.com are different origins because the host differs. Likewise, http://app.example.com and https://app.example.com are different because the protocol differs.
This restriction exists for a reason. Without it, a malicious site could load a user’s bank page in the background and try to steal data, trigger actions, or read private responses. The browser blocks that kind of cross-origin access by default to reduce the impact of script injection, token theft, and other web-based attacks. That default protection is one of the reasons the web is usable at all.
Simple examples of same-origin versus cross-origin
- Same-origin:
https://portal.example.com/accountandhttps://portal.example.com/settings - Cross-origin:
https://portal.example.comandhttps://api.example.com - Cross-origin:
https://example.comandhttps://example.com:8443 - Cross-origin:
http://example.comandhttps://example.com
The security idea is simple: a page should not automatically gain access to another site’s data just because the browser can reach it. That is why CORS policy exists in the first place. It is the controlled exception to a strict rule.
For deeper technical background, Microsoft’s browser and web security documentation at Microsoft Learn and NIST’s web application security guidance at NIST CSRC provide useful reference points for secure design.
What CORS Is and Why It Exists
CORS is a browser-supported mechanism that relaxes same-origin restrictions in a controlled way. It does not remove the same-origin policy. It gives servers a way to say which origins may read the response, which methods are allowed, and which headers can be sent.
That distinction matters. The server is not “turning off security.” The browser is still the enforcement point. A response without the right CORS headers may still travel across the network, but the browser will block JavaScript from accessing it. This is why the server can answer a request and still leave the developer with a CORS error.
CORS exists because modern applications depend on distributed services. A single-page app may call an API on another domain, pull fonts from a CDN, send telemetry to a third-party endpoint, or authenticate through an external identity provider. Without CORS, those common patterns would fail or require brittle workarounds.
Where CORS shows up in real projects
- Frontend apps calling APIs: React, Angular, or Vue apps often run on one host and call a REST or GraphQL API on another.
- Third-party integrations: Payment gateways, map services, and analytics tools often involve cross-origin communication.
- Microservices: Separate services may expose APIs across different subdomains or cloud endpoints.
- CDN assets: Fonts, images, and scripts are frequently served from a different origin.
Official guidance from WHATWG Fetch and practical examples in MDN are the best sources for how browsers actually apply the policy.
Key Takeaway
CORS is not a server-side permission switch. It is a browser check based on response headers.
How CORS Requests Work Behind the Scenes
A CORS request starts when the browser adds an Origin header to the outgoing request. The server receives that request, evaluates the origin, and returns a response with one or more Access-Control-* headers if it wants the browser to permit access. The browser then decides whether JavaScript can read the response.
This is the part many developers miss: a request can succeed on the network and still fail in JavaScript. The browser may send the request, receive the payload, and then hide the response from the page because the CORS policy was not satisfied. That is why network tools and browser console errors both matter when debugging.
The browser also distinguishes between sending a request and reading the response. Some cross-origin resources can load even when the response is not readable by script. For example, images, stylesheets, and scripts can often be requested cross-origin under different rules than AJAX calls. That is why an HTML page may visually load a resource while an API call using fetch() or XMLHttpRequest fails with an access error.
Example request and response flow
- The browser sends a request with
Origin: https://app.example.com. - The server responds with
Access-Control-Allow-Origin: https://app.example.com. - The browser checks whether the origin is allowed.
- If the check passes, JavaScript can read the response body.
- If the check fails, the browser blocks access even if the server returned HTTP 200.
That enforcement model is why ajax access-control-allow-origin issues often show up as browser-side errors instead of server errors. The server may be fine. The browser simply does not trust the cross-origin response enough to expose it to the page.
For implementation details, the official references are MDN CORS guide and the Fetch Standard.
CORS Headers You Need to Know
Most CORS problems come down to a small set of headers. If you understand them, you can usually diagnose failures quickly. If you misunderstand them, you will keep chasing false fixes like changing frontend code when the backend header is wrong.
Access-Control-Allow-Origin tells the browser which origin may read the response. It can be set to a single origin, such as https://app.example.com, or in some cases to * for public, non-credentialed access. If the browser sees a mismatch, the response is blocked.
Access-Control-Allow-Methods lists the HTTP methods the server accepts for cross-origin requests, such as GET, POST, PUT, or DELETE. Access-Control-Allow-Headers tells the browser which custom request headers are allowed. This is the header behind many allow headers cors questions, especially when developers send Authorization, X-Request-ID, or app-specific headers.
Important CORS headers and what they do
| Header | Purpose |
| Access-Control-Allow-Origin | Declares which origin may read the response. |
| Access-Control-Allow-Methods | Lists allowed HTTP methods for cross-origin requests. |
| Access-Control-Allow-Headers | Lists allowed request headers, including custom headers. |
| Access-Control-Allow-Credentials | Allows cookies, client certificates, or HTTP authentication to be included. |
| Access-Control-Max-Age | Lets the browser cache preflight results for a period of time. |
Access-Control-Allow-Credentials requires care. If you allow credentials, you cannot use a wildcard origin in a permissive way. That is one reason access-control-allow-origin not working complaints often trace back to a credentials mismatch rather than a broken server.
Warning
Do not use * for Access-Control-Allow-Origin when your application sends cookies or other credentials. That combination is rejected by browsers.
Official header behavior is documented in MDN and the browser-facing rules are defined by the WHATWG Fetch Standard.
Preflight Requests and When They Happen
A preflight request is an automatic OPTIONS request the browser sends before the actual cross-origin request. The browser uses it to ask the server whether the real request is allowed. This happens when the request is considered non-simple or potentially risky.
Browsers trigger preflight when you use methods like PUT, PATCH, or DELETE, when you send custom headers, or when the content type falls outside the small set of simple values. A common example is an API request with Authorization: Bearer ... or X-API-Key. In both cases, the browser wants confirmation before sending the real request.
Simple requests, such as a straightforward GET with standard headers, often avoid preflight. But the moment the request becomes more customized, the browser adds the preflight step. This is why people sometimes think “CORS broke after adding one header.” In reality, the browser is doing exactly what it is supposed to do.
What the server must return on preflight
- Access-Control-Allow-Origin matching the requesting origin.
- Access-Control-Allow-Methods including the requested method.
- Access-Control-Allow-Headers including any custom request headers.
- Optionally, Access-Control-Max-Age to cache the approval.
When access-control-allow-headers * allowed is being discussed, remember that browser support and security policies depend on the exact request pattern. A wildcard can simplify public APIs, but it is not a blanket solution for authenticated workloads.
The official browser behavior is described in MDN’s preflight documentation. For broader API security design, NIST guidance at NIST CSRC remains useful.
Common CORS Use Cases in Real Applications
Most teams run into CORS the first time they split a frontend and backend into separate services. A typical pattern is a browser app on app.example.com calling an API on api.example.com. That setup is normal, but it is cross-origin, so the API has to return the right headers or the browser blocks access.
CORS also appears in microservices and multi-domain architectures. A company may expose different services for user accounts, billing, reporting, and notifications. Each service may live behind a different host or gateway. If the browser app needs to talk to all of them, each one needs to participate in the CORS policy correctly.
CDNs are another common case. Fonts, stylesheets, JavaScript bundles, images, and video assets are often served from a different origin for performance and caching reasons. In that environment, CORS helps the browser decide whether a resource can be used safely. Third-party login flows, analytics scripts, and payment integrations also rely on cross-origin communication, though each service applies different security controls.
Examples you will recognize
- SPA to API: A React app sends fetch requests to a REST API on another subdomain.
- GraphQL gateway: A browser app queries a centralized API gateway that proxies multiple services.
- Payment processing: The browser redirects or posts data to a payment provider that must validate origin behavior.
- Authentication: Token flows and session checks often involve redirects and cross-origin endpoints.
For infrastructure-heavy applications, review MDN and the relevant platform documentation from your cloud or framework vendor. If you are documenting architectural decisions, pairing those details with CISA guidance on secure web exposure is a good practice.
Security Benefits and Limitations of CORS
CORS improves security by preventing random websites from reading sensitive responses from your application. That protects users from malicious pages trying to siphon data from authenticated sessions. In practice, CORS acts as a browser-side gatekeeper for cross-origin reads.
But CORS is not authentication. It is not authorization. It is not CSRF protection. A well-configured CORS policy can still coexist with a vulnerable application if session handling, token validation, or server-side access control is weak. That is where many teams make a mistake: they treat CORS as if it were a complete security boundary.
Improper configuration can create serious risk. If you reflect any origin blindly, allow credentials with overly broad rules, or accept headers and methods too loosely, you can expose private data to untrusted sites. On the other hand, a tight policy that is too restrictive can break legitimate integrations and drive teams toward unsafe workarounds.
CORS protects the browser’s decision to expose data. It does not protect the API from direct abuse by scripts, bots, or authenticated attackers.
That difference matters. A malicious site may be blocked from reading a response in the browser, but the same endpoint might still be callable directly if authentication and authorization are weak. For a full defense strategy, pair CORS with server-side controls, CSRF protections, strong session management, and input validation. OWASP’s Web Security Testing Guide and OWASP Top 10 are useful references.
For workforce and security context, the NICE/NIST Workforce Framework is a useful way to map web security responsibilities across development, operations, and security teams.
Common CORS Problems and Misconfigurations
The browser message “blocked by CORS policy” is a symptom, not a root cause. It usually means the response headers did not match the browser’s expectations. That could be an origin mismatch, a missing allow header, a credentials issue, or a preflight response that failed to include the right methods or headers.
One common mistake is using a wildcard origin with credentialed requests. Another is forgetting that preflight requests need their own correct response. A server can return the right CORS headers on the actual request and still fail because the preflight response was incomplete. This is especially common when a reverse proxy, load balancer, or API gateway strips headers or handles OPTIONS differently.
Environment mismatches are another frequent source of pain. http://localhost:3000 is not the same as http://localhost:5173. http is not https. A staging API and a production API may share the same codebase but need different allowed origins. If the frontend and backend teams do not align these values, the browser will fail the request even though both systems work independently.
Typical mistakes to check first
- Wildcard origin with credentials: Not allowed by browsers.
- Missing preflight headers:
OPTIONSresponse does not include required Access-Control-Allow-* values. - Protocol mismatch: HTTP frontend calling HTTPS backend, or vice versa.
- Port mismatch: Local development ports do not match the CORS allow list.
- Proxy interference: Nginx, Apache, or an API gateway rewrites or removes headers.
If you are troubleshooting an access-control-allow-origin not working issue, start with the browser console, then inspect the network tab, then verify the server response headers. This sequence usually reveals the problem faster than changing code blindly.
Note
Real browser behavior matters more than what curl or Postman show. Those tools do not enforce CORS the way a browser does.
For secure deployment patterns, compare your setup with vendor docs from Microsoft Learn or MDN.
How to Configure CORS Correctly
The safest approach is to allow only the origins your application truly needs. If only one frontend uses the API, set one explicit origin instead of a broad list. If several domains are required, maintain a controlled allow list and review it regularly.
Configure allowed methods and headers based on actual traffic, not guesses. If your app only uses GET and POST, do not allow DELETE just because it is convenient. If your frontend sends an Authorization header, make sure the server explicitly allows it. That is where many allow headers cors failures come from: the frontend works locally, then breaks as soon as a custom header is introduced.
For credentialed requests, be strict. Use a specific origin, not a wildcard. If you rely on cookies, make sure your session settings and CORS policy agree. If you are using token-based auth, verify whether the browser is sending credentials at all. Small mismatches here lead to confusing failures.
A practical configuration checklist
- List every origin that legitimately needs access.
- Allow only the HTTP methods the application actually uses.
- Permit only required request headers.
- Set credential handling deliberately, not by default.
- Test the policy in development and production separately.
When possible, keep the CORS policy narrow. Narrow policies are easier to audit and less likely to break when the application grows. For official implementation guidance, check the platform docs for your stack and compare them against the browser rules in MDN.
CORS in Popular Development Workflows
CORS problems show up constantly in local development. A frontend might run on localhost:3000 while an API runs on localhost:8080 or in a remote environment. Even though both are on your machine or in the same project, the browser treats them as different origins because the ports differ.
Development servers often use proxies to reduce friction. Instead of calling the API directly from the browser, the frontend dev server proxies requests to the backend, which makes the request appear same-origin from the browser’s point of view. That can be useful for local testing, but it does not replace proper CORS configuration in production.
API gateways and reverse proxies are also common places to centralize CORS rules. This can simplify management if multiple services share the same access pattern. The tradeoff is that the proxy becomes part of the security boundary, so it has to preserve the required headers and handle OPTIONS requests correctly.
Where to look when a dev build works but production fails
- Browser developer tools: Check the Console and Network tabs.
- Frontend proxy config: Confirm requests are actually being forwarded.
- API gateway rules: Make sure headers are not being dropped.
- Environment variables: Verify the app is calling the right endpoint.
Different frameworks expose CORS settings in different places, so always check official documentation rather than guessing. If you are using Microsoft-based stacks, Microsoft Learn is the right reference. For browser behavior, stick with MDN.
Debugging and Testing CORS Issues
The fastest way to debug CORS is to inspect the exact request and response that the browser sees. Open browser developer tools, go to the Network tab, and look for the original request plus any OPTIONS preflight call. Then compare the Origin request header with the Access-Control-Allow-Origin response header.
If there is a preflight request, inspect it separately. The browser will only continue if the server returns the right methods and headers. Many developers fix the actual request response and forget the preflight response still fails. That is why a request can look correct in one place and still fail in the browser.
Backend tools like curl and Postman are useful, but they do not enforce browser CORS rules. They can confirm that the server is reachable and that the response contains certain headers, but they cannot tell you whether the browser will accept that response for JavaScript access. Always test with a real browser when validating CORS.
Practical troubleshooting checklist
- Confirm the exact requesting origin.
- Check whether the request triggered preflight.
- Verify Access-Control-Allow-Origin matches the origin.
- Confirm allowed methods include the request method.
- Confirm allowed headers include every custom header.
- Review credential settings on both browser and server.
- Check for proxy, gateway, or CDN header stripping.
For security-oriented debugging habits, OWASP testing guidance at OWASP WSTG is a strong reference. For browser implementation details, MDN remains the practical standard.
Best Practices for Safer CORS Policies
The safest CORS policy is the one that gives only the access your application needs and nothing more. Start with a narrow allow list, then expand only when a real business requirement appears. That is much better than deploying a broad policy and hoping nothing bad happens.
Limit methods and headers to the smallest working set. If your API only needs GET and POST, do not allow more. If you do not need custom headers, do not add them. If you do need credentials, use them intentionally and validate every related setting, including cookies, SameSite behavior, and server-side authorization.
Separate development and production policies when necessary. Local testing often needs looser rules, but those rules should not leak into production. Review CORS settings whenever domains, subdomains, proxies, or third-party integrations change. A policy that was correct six months ago may be wrong after one infrastructure update.
Safe policy habits that prevent future problems
- Use explicit origins: Avoid wildcards for private data.
- Keep headers narrow: Only allow what the app actually sends.
- Document trusted domains: Make review and auditing easier.
- Test every environment: Development, staging, and production should all be validated.
- Recheck after changes: CDN, gateway, or auth updates can break CORS.
If your organization follows broader security frameworks, align your CORS review process with NIST guidance and your internal change control process. That keeps CORS from becoming an afterthought.
Pro Tip
When a CORS issue appears suddenly, check recent changes to proxies, load balancers, auth middleware, and environment variables before rewriting application code.
Conclusion
CORS policy is the browser mechanism that allows controlled cross-origin access without abandoning the same-origin policy. It exists because modern web apps depend on APIs, third-party services, and distributed infrastructure, but the browser still needs strict rules to protect user data.
The key is to understand what CORS does and what it does not do. It controls whether JavaScript can read a cross-origin response. It does not replace authentication, authorization, CSRF defenses, or server-side validation. If your headers are wrong, the browser blocks access. If your backend controls are weak, CORS will not save you.
When you configure CORS carefully, you get the best of both worlds: secure browser enforcement and practical cross-origin communication. That means fewer broken integrations, fewer production surprises, and fewer risky workarounds. For teams that build and support web applications, understanding CORS is not optional.
If you want to go deeper, review the browser rules in MDN Web Docs, the technical standard in WHATWG Fetch, and your platform vendor’s documentation. ITU Online IT Training recommends validating CORS settings in real browsers, not just API tools, and treating every cross-origin rule as part of your security design.
CompTIA®, Microsoft®, and OWASP are referenced for educational context; their names are used under fair descriptive use where applicable.