Fixing CORS errors¶
CORS — Cross-Origin Resource Sharing — is a browser-side check. When JavaScript loaded from https://app.example.com calls https://api.example.com, the browser refuses to expose the response unless the API returns the right Access-Control-Allow-* headers. Barbacana's CORS support is disabled by default, so until you enable it the browser will block every cross-origin call.
Symptoms¶
The browser Console shows one of:
Access to fetch at 'https://api.example.com/users' from origin
'https://app.example.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
The Network tab usually shows the request as (failed) or a 200/204 OPTIONS whose response is missing Access-Control-Allow-Origin. The audit log shows nothing wrong — Barbacana isn't blocking the request, the browser is.
When you need CORS¶
Enable CORS on a route when:
- A browser SPA hosted at one origin calls an API at a different origin (different host or different port or different scheme).
- A
<script type="module">from one origin imports from another. - The
OPTIONSpreflight is failing — that is also CORS.
You do not need CORS when:
- The browser and the API are served from the same origin (e.g. both behind the same Barbacana hostname). Same-origin traffic doesn't trigger CORS at all.
- The caller is a server, CLI, mobile app, or
curl— only browsers enforce CORS.
Diagnose¶
Reproduce the preflight from the terminal so you see Barbacana's actual response:
curl -i -X OPTIONS https://api.example.com/users \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type, authorization"
A correctly configured route returns Access-Control-Allow-Origin: https://app.example.com and a list of allowed methods and headers. If those are missing, CORS isn't configured on the route the browser is hitting.
Fix — enable CORS on the route¶
Add a cors block to every route the browser calls directly:
routes:
- id: api
match:
paths: ["/api/*"]
upstream: http://api:8000
cors:
allow_origins: [https://app.example.com]
allow_methods: [GET, POST, PUT, PATCH, DELETE]
allow_headers: [Content-Type, Authorization]
allow_credentials: true
max_age: 3600
Reload Barbacana, hard-refresh the SPA, and the console errors should disappear. Barbacana answers the OPTIONS preflight automatically — the upstream doesn't need to handle it.
See CORS reference for every field.
Common mistakes¶
| Mistake | What you'll see | Fix |
|---|---|---|
allow_origins: ["*"] with allow_credentials: true |
"The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'" | List origins explicitly. Never combine * with credentials. |
Custom request header missing from allow_headers |
Request header field X-Request-Id is not allowed by Access-Control-Allow-Headers in preflight response |
Add every custom header the SPA sends — Authorization, X-Request-Id, etc. |
Restricted accept.methods and dropped OPTIONS |
Preflight returns 405; browser console shows a CORS error | Keep OPTIONS in accept.methods (it's there by default — only an issue if you set the list explicitly). |
Trailing slash or scheme mismatch in allow_origins |
Browser console shows the exact Origin it sent — it doesn't match the entry in your config |
Use the exact origin: https://app.example.com. No path, no trailing slash. http:// and https:// are different origins. |
Multiple frontends, single cors block |
Some origins work, others fail | List every browser origin in allow_origins, or split frontends into separate routes. |