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:
GETHEADPOST
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:
AcceptAccept-LanguageContent-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-urlencodedmultipart/form-datatext/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, orDELETE - 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.
Top comments (2)
Regarding preflight requests, how do CORS headers impact the browser's decision to send an OPTIONS request? I'd love to swap ideas on this.
The browser makes this decision on its own, based on the request itself — independently of CORS. And let's not forget, CORS is about protecting the response, while preflight is about protecting the server from potentially dangerous requests.