CORS Headers Builder
Build a full set of CORS headers for an API: Access-Control-Allow-Origin, Methods, Headers, Credentials and Max-Age.
CORS in depth: origins, preflight, credentials and the headers that actually matter
CORS (Cross-Origin Resource Sharing) is the mechanism that lets a page on origin A make HTTP requests to an API on origin B. It is part of the WHATWG Fetch standard, descended from the W3C CORS recommendation, and enforced entirely by the browser — the server only suggests what it allows, the browser decides whether to expose the response to JavaScript.
An origin is the tuple scheme + host + port — https://example.com:443 is one origin, http://example.com is another, and https://api.example.com is a third. By default the same-origin policy blocks scripts from reading responses across origins; CORS is the controlled opt-in that lets servers say "yes, that origin can read this".
Simple requests vs preflight
A simple request uses GET, HEAD or POST, only standard headers, and a Content-Type of application/x-www-form-urlencoded, multipart/form-data or text/plain. The browser just sends the request with an Origin: https://app.com header and the server replies with Access-Control-Allow-Origin: https://app.com (or * for public APIs).
Anything else — PUT, DELETE, PATCH, custom headers like Authorization or X-API-Key, or a JSON Content-Type — triggers a preflight. The browser sends an OPTIONS request first and the server must answer with the full set of allow headers:
Access-Control-Allow-Origin: https://app.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Max-Age tells the browser how long to cache the preflight (in seconds) — without it, every state-changing request fires two round-trips. Chrome caps at 7200, Firefox at 86400.
Credentials: cookies and Authorization
When the client sends cookies, HTTP auth or a fetch(..., { credentials: 'include' }) request, the server must reply with Access-Control-Allow-Credentials: true. Critically, the Origin header in the response can NEVER be * when credentials are allowed — it must be the literal origin that called you. Most frameworks implement this as "reflect the request's Origin if it is in the allow-list".
To let your JS read non-standard response headers (pagination, rate-limit counters), expose them: Access-Control-Expose-Headers: X-Total-Count, X-Pagination, X-RateLimit-Remaining. Without this header the browser hides them even though they reach the network layer.
Configuring CORS in real servers
- Express:
app.use(cors({ origin: ['https://app.com'], credentials: true, maxAge: 86400 }))— thecorsnpm package handles OPTIONS automatically. - nginx:
add_header Access-Control-Allow-Origin "$http_origin" always;plus a dedicatedlocationblock that returns 204 for OPTIONS. - AWS CloudFront: attach a Response Headers Policy to the behaviour; CloudFront forwards
Originonly if you whitelist it in the cache key. - AWS API Gateway: enable CORS per-resource — it generates a mock OPTIONS integration.
- S3 static hosting: configure the bucket-level
CORSConfigurationXML (separate from bucket policy).
Common pitfalls
Allow-Origin: *combined withAllow-Credentials: true— the browser silently blocks the response. Reflect the specific origin instead.- The CORS spec does not allow multiple values in
Allow-Origin— you must echo a single origin from your allow-list (the reflect-origin pattern). - CDN cache without
Vary: Origincan serve the wrong CORS response to a different origin — always add the Vary header. - Preflight failures are easy to miss — the OPTIONS request returns 200 but the browser still blocks the real request because
Allow-Methodswas incomplete. - Redirects do not preserve the
Originheader on preflight in older browsers — avoid 301/302 on API routes.
FAQ
Why do I see "CORS error" in the console? The server did not return an Access-Control-Allow-Origin header matching your page's origin, or did not respond 2xx to the preflight. Inspect the Network tab — the OPTIONS request will show exactly which header is missing.
Can I bypass CORS from the client side? No. CORS is enforced by the browser; only the server can authorise the read. Workarounds (CORS proxies, browser flags) only work in development. In production, fix the server or move the call server-side.
Does preflight happen on every request? Only on non-simple requests, and only if the cache is cold. Set Access-Control-Max-Age to several hours and the browser reuses the result for that origin/method/header combination.
Is CORS a security feature? It is a relaxation feature, not a hardening one. It allows cross-origin reads that the same-origin policy would block. It does not protect a server from CSRF, XSS, or direct curl/Postman calls — those don't go through a browser.
How do I support multiple origins? Maintain an allow-list, check the incoming Origin against it, and reflect that exact value back. Always add Vary: Origin so caches segment responses correctly.
Related Tools
Fake Paginated JSON Generator
Generate a fake paginated JSON response (with page, pageSize, total and items) to mock a REST API. Speed up front-end development and testing.
Invoice Header Generator
Generate the HTML header of a professional invoice: logo, company name, tax data, number and date. Print/PDF-ready.
Token / API Key Generator
Generate cryptographically secure random tokens: hexadecimal, alphanumeric, Base58 or UUID. Ideal for API keys, secrets and session tokens.
Vue Component (Composition API)
Generate Vue 3 Composition API component skeleton (<script setup>) with props/ref/computed.
UUID v8 Custom Generator
Generate custom UUIDs v8 (RFC 9562) with arbitrary 122-bit payload useful for hierarchical or shard-aware IDs.
Random Advice Generator
Get a random piece of advice to reflect on and find inspiration in everyday life. A nice little nudge of motivation to share or start your day right.