CORS for Self-Hosted APIs: Allow the Right Browser Origins
CORS governs browser access to cross-origin responses. Keep its allowlist precise while retaining server-side authentication and authorization.
A front end at app.example.com calling an API at api.example.com makes a cross-origin browser request. CORS provides a way for the API to tell the browser which origins may read particular responses. It does not authenticate the caller, and it does not stop a non-browser client from sending a request. Keep it separate from the API's authorization model.
List the origins that should use the API
An origin includes scheme, hostname, and port. A development server with a different port is a different origin even when it runs on the same machine. Record production, staging, and local development origins separately, and keep development allowances out of production unless there is an explicit operational need.
Use exact, parsed comparisons through your framework's supported CORS implementation. A loose suffix comparison can accidentally accept a hostile lookalike domain. Do not reflect any supplied Origin value into an allow header. Decide who owns the allowlist so a front-end hostname change has a clear review path.
Choose credential behavior deliberately
Cookie-authenticated requests introduce both CORS and cookie-policy considerations. A wildcard origin is not the permitted response pattern for credentialed CORS access. Configure the specific allowed origin and credential behavior required by the application, then retain the application's CSRF protections. Authorization must still be checked on every request and every resource.
For a public read-only API, a broader response-sharing policy may be appropriate, but public readability does not imply public mutation rights. Keep administrative endpoints and authenticated account data out of an indiscriminate site-wide policy. Make the distinction visible in route ownership and tests.
Understand preflight without weakening the endpoint
Some browser requests first send an OPTIONS preflight to ask whether the intended method and headers are allowed. Inspect that request in browser developer tools. A failed preflight can explain why the application handler never receives the eventual request, while an actual response missing CORS headers can cause a different browser error.
Test an example such as the production front end submitting a permitted JSON request to its own API. Verify the requested method and headers are covered and that the actual request remains authenticated. Do not add every method and header merely because a preflight failed once. If responses vary by origin, configure caching behavior correctly so one origin's permission is not reused for another.
Test denied origins and error responses
Use your staging environment to try an unapproved origin and confirm that the browser cannot read protected responses. Independently test that the server rejects unauthorized requests even from an otherwise allowed origin. Check validation errors, expired sessions, and server errors because missing headers there can obscure useful diagnostics. Keep tokens and cookie values out of screenshots and incident notes.
Keep a server-side request test alongside the browser test. The first confirms authentication and database effects, while the second confirms browser response sharing. Together they prevent a successful CORS check from being mistaken for proof that the API's ownership rules are correct.
The Mozilla CORS guide explains browser enforcement. Our LayerOne client API article offers API usage context, and the documentation hub covers the underlying platform.