On this page
- What is an origin?
- Why browsers have this restriction
- A simple CORS request
- Why API keys often trigger a preflight request
- Why it works in curl but not in the browser
- CORS is enforced by the browser, but configured by the server
- CORS is not authentication
- Should an AI provider allow browser CORS?
- Never solve CORS by publishing your own shared API key
- Direct browser API vs server proxy
- Why native apps do not hit the same browser CORS wall
- CORS and local AI servers
- CORS and remote MCP servers
- Common CORS error patterns
- No Access-Control-Allow-Origin
- Preflight request failed
- Header not allowed
- Method not allowed
- Credentials/wildcard mismatch
- A systematic debugging sequence
- Check the browser Network panel
- Compare with curl
- Test the exact origin
- Should you set Access-Control-Allow-Origin: *?
- Avoid random public “CORS proxy” websites
- What this means for BYOKchat Web
- Bottom line
- Further reading
If an AI API works in curl, Postman, or a native app but fails when JavaScript calls it from a browser, the problem may be CORS rather than your API key or model configuration.
CORS—Cross-Origin Resource Sharing—is a browser security mechanism. It lets a server explicitly tell the browser which web origins are allowed to read cross-origin responses.
The key fact is:
curl / native app
→ sends HTTP request directly
→ no browser CORS enforcement
browser JavaScript
→ browser enforces same-origin policy + CORS
→ server must permit the web origin
This is why copying a working API base URL into a web BYOK client does not guarantee that the browser can use it.
What is an origin?
For browser security, an origin is based on the URL scheme, host, and port.
These are different origins:
https://chat.byok.pro
https://api.example.com
http://localhost:11434
http://192.168.1.50:1234
When JavaScript running on https://chat.byok.pro requests https://api.example.com, that is a cross-origin request.
The browser decides whether the calling page is allowed to read the response according to the server’s CORS headers and the request type.
Why browsers have this restriction
Without same-origin protections, any malicious website you visit could try to make authenticated requests to other sites or local services from your browser and read the results.
Imagine this page:
https://evil.example
silently trying to read:
https://bank.example/account
http://localhost:1234/v1/models
http://192.168.1.20/admin
Browsers need boundaries around what one origin can read from another.
CORS is a controlled way for the target server to say:
"I intentionally allow JavaScript from this origin to read my response."
It is not merely an annoying setting that browsers invented to break APIs.
A simple CORS request
Suppose BYOKchat Web is loaded from:
Origin: https://chat.byok.pro
and tries to call:
https://api.example.com/v1/models
A simplified successful response might include a header such as:
Access-Control-Allow-Origin: https://chat.byok.pro
The browser can then allow the page to read the response, assuming the rest of the CORS rules are satisfied.
If the server does not return the required permission, the browser can block JavaScript from accessing the response even though the network request may have reached the server.
Why API keys often trigger a preflight request
AI APIs commonly use an Authorization header and JSON request bodies. Those requests frequently require the browser to perform a CORS preflight before sending the real request.
The browser first sends an OPTIONS request that asks whether the actual request is allowed.
Conceptually:
Browser
│
│ OPTIONS /v1/chat/completions
│ Origin: https://chat.byok.pro
│ Access-Control-Request-Method: POST
│ Access-Control-Request-Headers: authorization, content-type
▼
API server
│
│ Access-Control-Allow-Origin: https://chat.byok.pro
│ Access-Control-Allow-Methods: POST, OPTIONS
│ Access-Control-Allow-Headers: Authorization, Content-Type
▼
Browser
│
│ POST /v1/chat/completions
│ Authorization: Bearer …
▼
API server
If the OPTIONS request fails, omits the necessary headers, redirects unexpectedly, or is not handled at all, the browser may never send the actual model request.
Why it works in curl but not in the browser
curl does not implement the browser same-origin security model.
So this can succeed:
curl https://api.example.com/v1/models \
-H "Authorization: Bearer …"
while browser JavaScript fails with a CORS error.
That tells you something useful:
Server reachable? probably yes
Credential valid? maybe
Browser permitted? not necessarily
Do not rotate a perfectly good API key merely because the browser console says “blocked by CORS.”
CORS is enforced by the browser, but configured by the server
A common misconception is that the web app can “enable CORS” for a remote API.
It cannot.
The server being called must return the relevant CORS response headers. JavaScript cannot grant itself permission to bypass the browser’s security policy.
This means fixes such as these do not solve the real problem:
fetch(..., { mode: "no-cors" })
no-cors produces an opaque response that JavaScript generally cannot inspect as a normal API response. It is not a way to turn an incompatible JSON API into a usable one.
Likewise, adding an Access-Control-Allow-Origin header to the request does not help. That is a response policy controlled by the server.
CORS is not authentication
CORS and API authentication solve different problems.
Authentication
"Is this caller allowed to use the API?"
CORS
"May JavaScript from this browser origin read the response?"
A server can require a valid API key and allow a specific web origin.
It can also allow CORS broadly while still rejecting requests without valid authentication.
Do not use permissive CORS as a substitute for authentication on a sensitive local or remote service.
Should an AI provider allow browser CORS?
It depends on the provider’s intended architecture and threat model.
Some APIs are intentionally usable directly from browser applications. Others assume credentials are stored on a backend server and therefore do not permit arbitrary browser origins.
A provider may be cautious about browser CORS because exposing a secret developer-owned API key in shipped frontend code is dangerous.
This is where BYOK changes the context: the user supplies their own credential locally rather than the website shipping one shared developer secret to every visitor.
But provider policy still wins. If the API does not support direct browser clients, a BYOK web app cannot safely pretend otherwise.
Never solve CORS by publishing your own shared API key
This architecture is unsafe:
JavaScript bundle
└── hard-coded developer API key
│
└── visible to every user who downloads the app
CORS permission does not hide a secret embedded in frontend code.
For a public application using one developer-owned credential, a server-side backend/proxy is usually required so the secret stays on infrastructure you control.
In a true BYOK web client, the user enters their own provider credential. Even then, the browser and provider must support the direct connection.
Read How to Store API Keys Safely.
Direct browser API vs server proxy
When a provider does not support browser CORS, one common architecture is a backend proxy:
Browser
│
▼
Your server
│
▼
AI provider
The server is not subject to browser CORS when it makes the provider request.
But a proxy changes the privacy and security boundary. If prompts or credentials pass through that server, your infrastructure becomes part of the trust chain.
Compare:
Direct BYOK browser path
browser → provider
Proxy path
browser → application server → provider
The proxy can add useful functionality—authentication, billing, policy enforcement, caching, auditing—but it is not free from privacy consequences.
See Direct-to-Provider AI vs Proxy Servers.
Why native apps do not hit the same browser CORS wall
CORS is part of the browser web-security model. A native iOS or macOS networking stack is not a web page making a cross-origin fetch, so browser CORS headers do not gate normal native HTTP requests in the same way.
That is one practical difference between a web BYOK client and a native one:
Web app
provider must support browser-origin access
Native app
no browser CORS requirement
but normal TLS/network/platform security rules still apply
This does not mean native apps may ignore provider authentication, TLS, or API policy. It simply removes the browser CORS layer.
Read BYOK Web App vs Native App: What Changes?.
CORS and local AI servers
Local AI is where CORS surprises many users.
You may run Ollama, LM Studio, llama.cpp, or another server on your Mac and confirm that this works:
curl http://localhost:1234/v1/models
Then you open a web client on another origin and it fails.
Possible reasons include:
- the server does not permit that browser origin;
- the preflight
OPTIONSrequest is not handled; Authorizationis not listed in allowed headers;- the server is bound only to loopback;
- the browser is on another device and
localhostpoints to that device itself; - HTTPS mixed-content or private-network restrictions apply;
- a firewall blocks the LAN request.
CORS is only one layer of local-network troubleshooting.
Read How to Debug Local AI Connection Failures and Why localhost Does Not Work From Your Phone.
CORS and remote MCP servers
Browser-based MCP clients face the same basic constraint.
If a remote MCP endpoint is called directly from browser JavaScript, the server must permit the web origin and required HTTP methods/headers.
A tool server can be perfectly valid according to the MCP protocol and still be unusable from a browser because its HTTP deployment lacks browser CORS support.
This distinction is useful:
MCP protocol compatible ✓
Browser HTTP/CORS compatible ?
Both must be true for a direct web MCP connection.
Common CORS error patterns
Browser messages vary, but these patterns are common.
No Access-Control-Allow-Origin
The server did not authorize your web origin to read the response.
Preflight request failed
The browser’s OPTIONS request was rejected, redirected, timed out, or returned incomplete CORS permission headers.
Header not allowed
The actual request wants to send a header such as Authorization, but the preflight response did not allow it.
Method not allowed
The browser wants to send POST, DELETE, or another method that the CORS policy did not permit.
Credentials/wildcard mismatch
Requests involving browser-managed credentials have stricter rules around wildcard origins. Do not blindly combine * with credentialed flows.
A systematic debugging sequence
When a web AI connection fails, debug in layers:
1. DNS / address correct?
2. Server reachable at all?
3. TLS / HTTPS valid?
4. API endpoint path correct?
5. CORS preflight succeeds?
6. Required headers allowed?
7. Authentication valid?
8. Model ID valid?
9. Request schema supported?
10. Streaming parser compatible?
This order prevents you from treating every failure as an API-key problem.
Check the browser Network panel
Look for the OPTIONS request and the actual request separately.
If only OPTIONS appears, inspect its status and response headers.
If the POST is sent and returns a provider JSON error, CORS may already be working and the problem is elsewhere.
Compare with curl
A successful curl request proves useful pieces of the path, but not browser compatibility.
Test the exact origin
A server that allows:
http://localhost:3000
may still reject:
https://chat.byok.pro
Origins are exact security identities, not vague domain families.
Should you set Access-Control-Allow-Origin: *?
Only if that policy genuinely fits the service.
For a public unauthenticated API, a wildcard may be appropriate. For a sensitive local management endpoint, it may be far too broad.
Prefer designing CORS from the service’s threat model:
Who should call this endpoint?
From which web origins?
With which methods?
With which headers?
With what authentication?
Do not copy a permissive CORS snippet solely because it makes the browser error disappear.
Avoid random public “CORS proxy” websites
A public CORS proxy can make an incompatible API appear reachable by forwarding the request through someone else’s server.
For an AI API request, that can expose:
- your API key;
- prompts and chat context;
- attachments;
- model responses;
- tool data.
The data path becomes:
browser → unknown proxy → AI provider
That is usually a terrible tradeoff for a credentialed BYOK workflow.
If you truly need a proxy, use infrastructure you control and understand the new trust boundary.
What this means for BYOKchat Web
BYOKchat Web is designed for direct provider and remote-MCP communication where the endpoint supports browser access.
That architecture has a strong privacy advantage: BYOK infrastructure does not need to proxy every AI prompt merely to make the product function.
The tradeoff is that a provider or custom endpoint that refuses browser CORS may work in native BYOKchat but not in the web version.
That is a real platform limitation, not something the UI should hide.
Bottom line
If an AI API works in curl but fails in a browser, check CORS before blaming the API key.
CORS is the server-controlled permission layer that tells a browser whether JavaScript from one origin may read a response from another origin. Native clients and command-line tools do not enforce browser CORS, which is why the same endpoint can behave differently across platforms.
The safe fixes are to use a browser-compatible provider, configure CORS correctly on an endpoint you control, use a trusted native client, or deliberately introduce a backend proxy whose privacy/security consequences you understand.
Do not bypass the problem with leaked frontend secrets or an unknown public CORS proxy.