1001Ferramentas
πŸ”“Generators

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 })) β€” the cors npm package handles OPTIONS automatically.
  • nginx: add_header Access-Control-Allow-Origin "$http_origin" always; plus a dedicated location block that returns 204 for OPTIONS.
  • AWS CloudFront: attach a Response Headers Policy to the behaviour; CloudFront forwards Origin only 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 CORSConfiguration XML (separate from bucket policy).

Common pitfalls

  • Allow-Origin: * combined with Allow-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: Origin can 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-Methods was incomplete.
  • Redirects do not preserve the Origin header 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