CORS Preflight Request Reference

OPTIONS preflight request/response header flow with wildcard and credential rules.

Reference and analyzer for the CORS preflight (OPTIONS) flow — Access-Control-Request and Access-Control-Allow header pairs, simple vs preflighted requests, wildcard and credential rules. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is a CORS preflight request?

A preflight is an automatic OPTIONS request the browser sends before the real cross-origin request, to ask the server whether the actual method and headers are allowed. The server answers with Access-Control-Allow-* headers; only if they permit the request does the browser send it.

The CORS preflight flow

Before a cross-origin request that is not “simple”, the browser sends an automatic OPTIONS preflight asking the server which method and headers are allowed. This reference pairs every Access-Control-Request-* header with the Access-Control-Allow-* response the server must return, and an analyzer tells you whether a given request preflights.

Why preflight exists — the underlying design

CORS preflight is not a performance feature. It is a security mechanism built around the Same-Origin Policy. Before CORS existed, browsers prevented cross-origin reads entirely. As the web evolved and APIs proliferated, a controlled way to opt in to cross-origin access was needed — but without enabling a malicious page to silently trigger state-changing requests (like a DELETE or a PUT) against a user’s authenticated session on another origin.

The preflight acts as an explicit consent check: the browser asks the server “are you willing to accept a cross-origin request using this method and these headers from this origin?” before sending the actual request. Servers that have not been configured to respond correctly to OPTIONS simply do not respond, and the real request never fires.

The two paths: simple vs. preflighted

A request takes the simple path (no preflight) when all three conditions hold:

  1. The method is GET, HEAD, or POST.
  2. Only CORS-safelisted request headers are used: Accept, Accept-Language, Content-Language, Content-Type (with restrictions), Range.
  3. Any Content-Type is one of: application/x-www-form-urlencoded, multipart/form-data, or text/plain.

Anything outside these constraints — including application/json as the Content-Type, any custom header like Authorization, or a method like PUT, DELETE, or PATCH — triggers a preflight.

How it works

The browser preflights when the request uses a non-safelisted method or header. The preflight is an OPTIONS carrying the intended method and headers:

OPTIONS /api/data HTTP/1.1
Origin: https://app.example
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type, authorization

The server must answer with matching allow headers:

Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Methods: GET, PUT, DELETE
Access-Control-Allow-Headers: content-type, authorization
Access-Control-Max-Age: 600

A request that uses only GET/HEAD/POST, safelisted headers and a safelisted Content-Type is “simple” and skips the preflight entirely.

The credentials exception — the most common misconfiguration

When a request sends credentials (cookies or HTTP authentication), Access-Control-Allow-Origin: * is not permitted. The browser will reject the response even if the server sends it. The server must echo the exact Origin value and also include Access-Control-Allow-Credentials: true.

This trips up developers who set a wildcard origin during development (where credentials may not be involved) and then add authentication later. The fix requires two changes simultaneously: replace * with the specific origin, and add the Allow-Credentials header.

The complete header pairing reference

Browser sendsServer must return
Access-Control-Request-Method: DELETEAccess-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Request-Headers: authorizationAccess-Control-Allow-Headers: authorization
Origin: https://app.exampleAccess-Control-Allow-Origin: https://app.example
(credentials flag set)Access-Control-Allow-Credentials: true (and specific origin, not *)
(any preflight)Access-Control-Max-Age: 600 (optional, caches result)

Tips and notes

  • With credentials, never use * — echo the exact Origin and set Access-Control-Allow-Credentials: true.
  • Access-Control-Max-Age caches the preflight result so the browser does not send an OPTIONS on every API call. Chromium caps the value at 7200 seconds; Firefox allows up to 86400 seconds.
  • List every custom request header in Access-Control-Allow-Headers — a single missing header causes the preflight to fail silently from the developer’s perspective but with a clear error in the browser console.
  • Access-Control-Expose-Headers is separate: it controls which response headers JavaScript can read. By default only a small set of safelisted response headers are accessible; add any custom headers here if your client code needs them.
  • Preflight failures appear in the browser network panel as a failed OPTIONS request, not as a failure on the actual intended request — check the OPTIONS response first when debugging.