DEV Community

What Is a Simple Request and When Does the Browser Send a Preflight Request?

In the previous article, we learned that browsers sometimes send an OPTIONS request before the actual Cross-Origin request. This process is known as a CORS Preflight Request. But does this happen for every request? The answer is no. Browsers only perform a Preflight Request when the request does not qualify as a Simple Request.

What Is a Simple Request?

A Simple Request is a Cross-Origin request that satisfies a specific set of conditions defined by the Fetch specification. Since these requests are considered low risk, the browser sends them directly without performing a Preflight check. CORS rules still apply to the response, but no preliminary OPTIONS request is needed.

Requirements for a Simple Request

A request is considered simple only if all of the following conditions are met.

1. HTTP Method

The request method must be one of:

  • GET
  • HEAD
  • POST

Methods such as PUT, PATCH, and DELETE automatically trigger a Preflight Request.

2. Request Headers

The request may only include CORS-safelisted request headers, such as:

  • Accept
  • Accept-Language
  • Content-Language

Adding headers like Authorization: Bearer <token> or X-API-Key: 123456 causes the browser to perform a Preflight Request.

3. Content-Type

If the request has a body, its Content-Type must be one of:

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

Using a content type such as application/json makes the request non-simple and triggers a Preflight Request. This is one of the most common reasons developers see OPTIONS requests in modern web applications.

Examples

This request is simple:

fetch("/users")

No Preflight Request is sent.

This request is also simple:

fetch("/login", {
  method: "POST",
  headers: { "Content-Type": "text/plain" }
})

Again, no Preflight is required.

However, this request is not simple:

fetch("/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" }
})

The browser first sends an OPTIONS request.

The same happens here:

fetch("/profile", {
  headers: { "Authorization": "Bearer token" }
})

Because the Authorization header is not CORS-safelisted, a Preflight Request is required.

Summary

A browser sends a Preflight Request only when a Cross-Origin request does not meet the requirements of a Simple Request. The most common triggers are:

  • Using methods such as PUT, PATCH, or DELETE
  • Including non-safelisted headers such as Authorization
  • Using content types like application/json

This is why seeing an OPTIONS request before the actual request is completely normal in many modern APIs.

Comments

No comments yet. Start the discussion.