When inspecting network traffic in browser developer tools, developers often notice an extra HTTP request using the OPTIONS method appearing just before their intended POST, PUT, or DELETE request.

This preliminary exchange is known as an OPTIONS preflight request.

Purpose of preflight requests

A web browser sends an OPTIONS preflight request automatically before certain cross-origin HTTP requests to determine whether the target server understands CORS and permits the requested method and headers.

If the preflight check succeeds, the browser proceeds to send the actual application request. If the server does not return acceptable CORS preflight headers or returns an error, the browser cancels the request before the actual payload is transmitted.

What a preflight request is

A preflight request is a safety mechanism built into modern web browsers under the Cross-Origin Resource Sharing (CORS) specification.

Before making a request that might cause side effects on a cross-origin server—such as creating, modifying, or deleting resources with non-standard headers or non-form data types—the browser initiates a lightweight preliminary OPTIONS request.

The preflight request asks the server: “Are you willing to accept a cross-origin request from my origin using this specific HTTP method and these request headers?”

What the browser sends in a preflight

When issuing a preflight check, the browser sends an HTTP OPTIONS request containing specialized metadata headers:

OPTIONS /v1/users HTTP/1.1
Host: api.example.test
Origin: https://app.example.test
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type

Key request headers included by the browser:

  • Origin: The scheme, host, and port of the web application making the call.
  • Access-Control-Request-Method: The HTTP method of the actual request (e.g., POST, PUT, DELETE).
  • Access-Control-Request-Headers: A comma-separated list of any non-safelisted headers the actual request will include (e.g., authorization, content-type, or custom headers like x-api-version).

Notice that the preflight request body is empty and does not carry authentication cookies or tokens.

Why some requests preflight

Not all cross-origin requests trigger a preflight. Requests that fall under the definition of a simple request do not preflight because HTML forms and standard hyperlinks have historically been able to send them across origins since the early web.

A request requires an OPTIONS preflight if it introduces capabilities that classic HTML elements could not perform:

  1. Non-simple HTTP methods: Methods other than GET, HEAD, or POST (such as PUT, PATCH, or DELETE).
  2. Custom or non-safelisted request headers: Any header other than the standard safelisted set (Accept, Accept-Language, Content-Language, Content-Type, Range). For example, sending an Authorization or X-Custom-Header header triggers a preflight.
  3. Non-safelisted Content-Type values: Sending a payload with a media type other than form data or plain text.

Content-Type, methods, and request headers

The Content-Type header plays a central role in preflight behavior. The CORS specification defines only three CORS-safelisted media types:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

While application/json is the standard and most prevalent data format in modern REST and GraphQL APIs, it is not a CORS-safelisted media type. Therefore, any cross-origin browser request sending Content-Type: application/json will automatically trigger an OPTIONS preflight request.

Additionally, adding an Authorization header with a Bearer token or API key will also trigger a preflight, even on a simple GET request.

What the server response must communicate

When the server receives an OPTIONS preflight request, it must return an HTTP response containing CORS headers that authorize the interaction:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.test
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

Required response headers:

  • Access-Control-Allow-Origin: Must match the requesting Origin or be * (if credentials are not used).
  • Access-Control-Allow-Methods: Must include the method specified in Access-Control-Request-Method.
  • Access-Control-Allow-Headers: Must include all headers specified in Access-Control-Request-Headers.
  • Access-Control-Max-Age (optional): Tells the browser how many seconds it may cache the preflight result, avoiding repeated preflight requests for subsequent calls.

How to inspect preflight in DevTools

To observe and inspect preflight requests:

  1. Open your browser’s DevTools and go to the Network tab.
  2. Ensure network filtering is set to All or Fetch/XHR.
  3. Trigger the cross-origin API call from your frontend application.
  4. Look for two consecutive entries for the same URL:
    • The first entry will show method OPTIONS and type preflight.
    • The second entry represents the actual request (POST, PUT, etc.).
  5. Click on the OPTIONS request to verify the Access-Control-Request-* headers sent by the browser and the corresponding Access-Control-Allow-* headers returned by the server.

Key takeaway

An OPTIONS preflight request is an automated browser safety check sent before cross-origin requests that use non-safelisted HTTP methods, custom headers, or non-form media types like application/json. The server must acknowledge and permit these parameters via Access-Control-* response headers before the browser will dispatch the real application request.