CORS Explained
CORS — Cross-Origin Resource Sharing — is the mechanism that lets a web page on one origin make requests to a different origin. It is one of the most common sources of confusion for frontend developers, but once you understand the underlying Same-Origin Policy and how CORS headers work, the errors become easy to fix.
The Same-Origin Policy
Browsers enforce the Same-Origin Policy (SOP): JavaScript on https://app.example.com can freely fetch resources from the same origin but is blocked from reading responses from https://api.other.com. SOP prevents malicious sites from stealing data from authenticated sessions on other domains.
How CORS Relaxes the Restriction
CORS is an opt-in mechanism. The server at api.other.com can include response headers that tell the browser "this origin is allowed to read my responses." The key header is:
Access-Control-Allow-Origin: https://app.example.com
If the header is present and matches the requesting origin, the browser allows the JavaScript to read the response. Without it, the browser blocks access and logs the familiar CORS error.
Simple vs Preflight Requests
A "simple" request (GET or POST with standard content types like application/x-www-form-urlencoded) is sent directly. But if you set custom headers, use methods like PUT or DELETE, or send application/json, the browser first sends an OPTIONS preflight request to ask the server what is allowed. The server responds with headers like:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
You can inspect the full preflight exchange by converting your fetch call to cURL with the cURL Converter and sending the OPTIONS method manually.
Common CORS Headers
Access-Control-Allow-Origin — which origins can read responses. Access-Control-Allow-Methods — which HTTP methods are permitted. Access-Control-Allow-Headers — which request headers are allowed. Access-Control-Allow-Credentials — whether cookies are included. Access-Control-Expose-Headers — which response headers JavaScript can read. Access-Control-Max-Age — how long the preflight result is cached.
Credentials and Cookies
By default, cross-origin requests do not include cookies. To include them, the client must set credentials: 'include' in the fetch call, and the server must respond with Access-Control-Allow-Credentials: true. Critically, when credentials are used, Access-Control-Allow-Origin cannot be the wildcard * — it must specify the exact origin.
Common Errors and Fixes
"No Access-Control-Allow-Origin header" — the server is not sending CORS headers; add them in your API framework. "Preflight response is not successful" — the server is not handling OPTIONS requests; add an OPTIONS handler. "Credential is not supported if the CORS header Access-Control-Allow-Origin is *" — switch from wildcard to explicit origin. Use the JSON Formatter to inspect API error response bodies, which often contain clues about misconfigured CORS middleware.
Server Configuration Examples
In Express: app.use(cors({ origin: 'https://app.example.com', credentials: true })). In Nginx: add the headers in a location block. In Flask: use the flask-cors extension. Always restrict the allowed origin to your actual frontend domain rather than using a wildcard in production. You can use the Code Diff tool to compare your development and production CORS configurations side by side.
CORS for APIs and CDNs
Public APIs often use Access-Control-Allow-Origin: * because they serve any client. CDNs serving fonts or scripts also need CORS headers so browsers allow the cross-origin load. S3, Cloud Storage, and Azure Blob all have CORS configuration panels. Be precise: only allow the methods and headers your clients actually need.