Skip to content
AZ Tools

CORS Headers Builder

Compose Access-Control-* response headers from a checklist, with four presets and live warnings for the dangerous combinations.

Presets
Allow-Methods
Response headers
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 600
Vary: Origin

Apply these on the response to preflight OPTIONS *and* the actual request. Edge caches need Vary: Origin if you echo Origin.

How to use

  1. Start from a preset that matches your scenario.
  2. Set the origin (* for fully-public, or echo the request's Origin server-side for credentialed APIs).
  3. Pick the methods and headers your endpoint actually accepts — narrower is safer.
  4. Copy the headers into your server, edge worker, or framework middleware.

About this tool

Show

Builds the response headers a server needs to send to satisfy a browser's cross-origin request. Includes Allow-Origin, Allow-Methods, Allow-Headers, Expose-Headers, Allow-Credentials, Max-Age, and a Vary: Origin hint. Flags the common foot-guns: combining * with credentials (browsers will block it), accepting Origin: null (an attacker can spoof it), or asking the browser to cache the preflight for unreasonably long.

The thing to hold on to is that CORS is enforced by the browser and not by your server. These headers do not protect a resource; they relax a restriction the browser would otherwise impose on script running on another site. Anything that is not a browser — curl, a backend service, a scraper — never consults them and gets the response regardless. If a URL should not be readable by everyone, it needs authentication, not a narrower Allow-Origin.

Access-Control-Allow-Credentials: true cannot be combined with *. The browser rejects the pair outright, so a server that wants cookies on a cross-origin request has to echo back the exact requesting origin. Echoing turns the response into something that varies by request, which is why Vary: Origin matters: without it a shared cache can store the response generated for one origin and hand it to the next, and the header it carries will name the wrong site.

Origin: null is not a wildcard and not a safe default. Browsers send it from sandboxed iframes, from file:// documents and after some redirects, and any page can arrange to be in one of those states, so allowing it is close to allowing everyone while looking restrictive.

Max-Age is a request, not a setting. Browsers cap how long a preflight result may be reused — the ceilings differ per browser and are well under a day in Chrome — so a value of 86400 is silently clamped rather than honoured. The header also only governs the preflight itself; the actual request that follows is checked on its own.

Frequently asked questions

Can I use * and credentials together?

No. The fetch spec rejects responses that pair Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true. Either drop credentials, or echo the actual request Origin server-side after validating it against an allow-list.

Why the Vary: Origin recommendation?

If you echo Origin into Allow-Origin, downstream caches must vary their stored response on the request's Origin header; otherwise they may serve one site's CORS response to another site's request. Vary: Origin tells caches to key on that header.

The preflight passes but the browser still blocks my request. Where do I look?

At the actual response, not the preflight. Both have to carry Access-Control-Allow-Origin — a browser rechecks the real response and blocks it if the header is missing, which happens easily when the preflight is handled by a proxy or a framework and the endpoint itself is not. The other frequent cause is a redirect: the response to the preflight must not redirect.

Why can my page not read a header that I can see in the network panel?

Because a cross-origin response only exposes a short safelist to JavaScript, and everything else needs naming in Access-Control-Expose-Headers. The panel shows what the browser received; the page sees what CORS permits. A pagination count, a rate-limit header or a request id all need to be exposed explicitly before code can read them.

Related tools