Backend

CORS Preflight Explained

Learn how CORS preflight requests work, including OPTIONS handling, allowed methods, headers, credentials, caching, errors, and backend mistakes.

A CORS preflight request is the browser asking a server for permission before sending certain cross-origin requests.

Backend developers usually notice preflights when an API works in Postman but fails in the browser, or when frontend requests suddenly trigger OPTIONS calls.

Quick answer

A CORS preflight is an OPTIONS request sent by the browser before the real request. It checks whether the target server allows the calling origin, HTTP method, request headers, and credential mode.

If the preflight response is missing the required CORS headers, the browser blocks the real request from frontend JavaScript.

Preflight is not authentication. It is a browser access-control check for cross-origin reads and writes. Your API still needs normal authentication, authorization, validation, and abuse controls.

When browsers send preflight

Browsers send a preflight when a cross-origin request is not considered a simple request.

Common triggers include:

  • Methods such as PUT, PATCH, or DELETE.
  • Request headers such as Authorization.
  • JSON requests with Content-Type: application/json.
  • Custom headers like X-Request-ID or Idempotency-Key.
  • Credentialed cross-origin requests in many practical setups.

Example browser flow:

OPTIONS /api/orders
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type, idempotency-key

The server is expected to answer whether that origin, method, and header set is allowed.

Required response headers

A typical preflight response looks like this:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE
Access-Control-Allow-Headers: authorization, content-type, idempotency-key
Access-Control-Max-Age: 600

The key headers are:

HeaderMeaning
Access-Control-Allow-OriginWhich origin may read the response.
Access-Control-Allow-MethodsWhich HTTP methods are allowed.
Access-Control-Allow-HeadersWhich non-simple request headers are allowed.
Access-Control-Allow-CredentialsWhether credentialed requests are allowed.
Access-Control-Max-AgeHow long the browser may cache the preflight result.

The response body usually does not matter. Many APIs return 204 No Content.

Credentials and cookies

Credentialed browser requests add more rules.

If the frontend sends cookies or uses credentials: "include", the server must usually return:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com

Do not combine credentialed requests with Access-Control-Allow-Origin: *. Browsers reject that combination.

Cookie-based authentication also needs secure cookie attributes. Read SameSite Cookies Explained, Cookie vs Token Authentication, and Cookie Security Checklist before relying on cross-origin cookies.

Handling OPTIONS in the backend

Many CORS bugs happen because the application never routes OPTIONS requests correctly.

A backend should handle preflight before normal endpoint logic blocks the request. In many frameworks, this belongs in middleware, a gateway, or an edge rule.

Good behavior includes:

  • Return CORS headers for allowed origins.
  • Return allowed methods and headers that match the real API.
  • Avoid requiring user authentication for the preflight itself.
  • Keep the origin allowlist explicit.
  • Return a clear status such as 204 or 200.
  • Avoid expensive database calls for preflight.

The real request still needs authentication. The preflight only answers whether the browser is allowed to proceed.

Preflight caching

Access-Control-Max-Age lets the browser cache a successful preflight result.

Access-Control-Max-Age: 600

This can reduce repeated OPTIONS traffic. The value is in seconds, though browser caps vary.

Use a modest cache time for stable APIs. Avoid very long cache times while actively changing CORS policy, because clients may keep old decisions longer than you expect.

Preflight caching is not the same as HTTP response caching for the API data. It only caches the permission check.

Security boundaries

CORS is not a substitute for backend security.

CORS controls whether browser JavaScript can read a cross-origin response. It does not replace:

  • Authentication.
  • Authorization.
  • CSRF protection.
  • Rate limiting.
  • Input validation.
  • Tenant checks.
  • Audit or security event logging.

A blocked CORS response may still mean the request reached the server. Do not rely on CORS to stop state changes. Design sensitive endpoints with proper authorization and CSRF defenses.

Read API Authentication Best Practices and API Abuse Prevention Explained for the API side of the control model.

Common mistakes

The first mistake is allowing every origin with credentials. That turns CORS into an accidental trust policy.

The second mistake is allowing Authorization in the real request but forgetting it in Access-Control-Allow-Headers.

The third mistake is requiring authentication on OPTIONS before CORS middleware runs.

The fourth mistake is returning CORS headers only on successful responses. Error responses also need CORS headers if the browser should expose them to the frontend.

The fifth mistake is treating Postman or curl tests as proof that browser CORS will work. Non-browser clients do not enforce CORS.

The sixth mistake is using CORS to hide data instead of enforcing authorization on the server.

Debugging checklist

When a preflight fails, check:

  • What is the exact Origin header?
  • Does the server allow that origin exactly?
  • What method is in Access-Control-Request-Method?
  • Are all requested headers allowed?
  • Are credentials being sent?
  • Is Access-Control-Allow-Credentials required?
  • Is Access-Control-Allow-Origin incorrectly set to *?
  • Does the route handle OPTIONS?
  • Do gateway, CDN, and app layers all preserve the needed headers?
  • Do error responses include CORS headers too?

Browser devtools usually show the failed preflight request. Inspect the OPTIONS response before debugging the real API call.

Read CORS Explained for Backend Developers for the broader CORS model, REST API Status Codes Explained for response behavior, and API Gateway Explained for centralized CORS handling.

For authentication and browser security, read Cookie vs Token Authentication, SameSite Cookies Explained, CSRF vs XSS, and API Authentication Best Practices. For the full sequence, browse the API Design Learning Path, Authentication Learning Path, and Backend Security Learning Path.