CORS Preflight

A CORS preflight is an `OPTIONS` request that a browser sends before certain cross-origin requests to ask the server whether the actual request is allowed. The server answers with CORS response headers describing permitted origins, methods, and request headers.

CORS (Cross-Origin Resource Sharing) controls whether browser JavaScript can access resources from a different origin. An origin is defined by scheme, host, and port, so changing any of those can make a request cross-origin.

Browsers perform a preflight when a cross-origin request does not qualify as a CORS "simple request." Common triggers include methods such as `PUT`, `PATCH`, or `DELETE`, and certain non-safelisted request headers such as `Authorization`. The browser sends an `OPTIONS` request containing headers such as `Origin`, `Access-Control-Request-Method`, and, when needed, `Access-Control-Request-Headers`.

The server must respond with appropriate CORS headers, commonly `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers`. If the response does not satisfy the browser's CORS checks, the browser prevents the frontend from making the actual request or from exposing its response to the JavaScript environment.

Preflight is a browser behavior, not an authentication mechanism or a general HTTP firewall. A server may receive requests from non-browser clients without those clients enforcing CORS. CORS primarily protects users by restricting browser-based cross-origin access to responses.

Servers can reduce repeated preflight requests by returning `Access-Control-Max-Age`, which tells the browser how long the successful preflight result may be reused, subject to browser-specific limits and CORS rules.

Key points

Interview questions

What is a CORS preflight request and why does the browser send it?

A CORS preflight is an `OPTIONS` request made by a browser before certain cross-origin requests. It tells the server which HTTP method and non-safelisted request headers the browser intends to use. The server responds with CORS headers indicating whether those characteristics are allowed. If the browser's CORS checks fail, the frontend cannot proceed as though it had permission to access the cross-origin response.

Which request characteristics commonly trigger a CORS preflight?

Preflight is associated with cross-origin requests that are not CORS-safelisted. A method such as `PUT`, `PATCH`, or `DELETE` commonly triggers it. Request headers outside the CORS-safelisted set can also trigger preflight, with `Authorization` being a common example. Certain content types and other request characteristics can likewise make a request non-safelisted.

What headers are involved in a typical CORS preflight exchange?

The browser includes `Origin` to identify the requesting origin. It sends `Access-Control-Request-Method` to state the method it plans to use and may send `Access-Control-Request-Headers` to list non-safelisted headers it plans to include. The server responds with headers such as `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers`. The browser evaluates those headers before deciding whether the actual request is permitted.

A frontend gets a CORS error even though the API endpoint works in Postman. Why?

Postman is not subject to browser CORS enforcement in the same way a web page is. A browser adds the requesting origin and applies the CORS protocol when JavaScript accesses another origin. The API may therefore work from Postman while failing browser CORS checks. The fix is generally to configure the server or relevant gateway to return appropriate CORS responses for the web application's origin and request characteristics.

How would you debug a failing CORS preflight in production?

I would inspect the browser's network panel and locate the `OPTIONS` request preceding the failing request. I would verify the request's `Origin`, requested method, and requested headers, then compare them with the server's `Access-Control-Allow-*` response headers and the HTTP status. I would also check whether a proxy, CDN, load balancer, or authentication middleware is intercepting `OPTIONS`. Finally, I would verify credential-related settings and caching behavior if those are involved.