Cross-Origin Resource Sharing (CORS) is a browser-enforced mechanism that controls which origins (scheme + host + port) can read responses from your API. The server sets Access-Control-Allow-Origin and related headers; the browser blocks disallowed responses. Preflight requests: for non-simple requests (e.g., custom headers, methods like PUT/DELETE, or Content-Type other than simple types), the browser issues an OPTIONS request with Access-Control-Request-Method/Headers. The server must respond with Access-Control-Allow-Methods, Access-Control-Allow-Headers, and optionally Access-Control-Max-Age to allow the actual request.
SameSite cookie semantics affect whether cookies are sent on cross-site requests:
- SameSite=Lax (default in many browsers) sends cookies for top-level GET navigations but not most cross-site XHR/fetch.
- SameSite=Strict blocks cookies entirely on cross-site navigations.
- SameSite=None; Secure allows cookies for cross-site requests (must be Secure).
Implications for SPAs calling your API:
- Browser clients cannot rely on cookies for cross-origin calls unless server sets SameSite=None; Secure and the client uses credentials: 'include' (fetch) and server returns Access-Control-Allow-Credentials: true.
- Allowing credentials requires Access-Control-Allow-Origin to be a specific origin (cannot be '*').
- Preflight adds latency for some calls — minimize by using simple requests or enabling appropriate CORS headers and caching via Access-Control-Max-Age.
Safe default CORS policy recommendations:
- Deny by default; explicitly allow only trusted origins.
- Use origin allow-listing (mirror the incoming Origin header if it’s on your allow-list).
- Do not use Access-Control-Allow-Origin: * when Access-Control-Allow-Credentials: true is needed.
- Restrict allowed methods and allowed headers tightly.
- Set Access-Control-Max-Age to reduce preflight overhead safely.
Design docs and SDKs to minimize CORS friction:
- Document required headers, whether credentials are needed, and exact allowed origins.
- Provide client examples using fetch/axios showing credentials: 'include' and how to set Content-Type to simple types to avoid preflight.
- Offer an SDK wrapper that:
- Proxies requests through a same-origin client (optional)
- Sets correct headers and handles token refresh
- Provides a CORS-safe default (use bearer tokens in Authorization header; OAuth against same-origin cookie-less flows to avoid cross-site cookies)
- Recommend using token-based auth (Authorization: Bearer) sent in headers rather than cookies when possible — simpler CORS posture (no credentials) and avoids SameSite complexity.
- Include troubleshooting checklist (inspect Origin, preflight OPTIONS response, Access-Control-Allow-* headers, browser console errors).
Summary guidance: prefer explicit origin allow-lists, token auth over cross-site cookies, document credentials requirements clearly, and provide SDKs/examples that set correct fetch/axios options to reduce developer friction and avoid insecure wide-open CORS policies.