CORS Preflight OPTIONS Requests in JavaScript
This article explains what Cross-Origin Resource Sharing (CORS)
preflight OPTIONS requests are, why modern web browsers
automatically trigger them during JavaScript fetch
operations, the conditions that cause them, and how back-end servers
must respond to handle them properly.
What Is a CORS Preflight Request?
A CORS preflight request is an automated HTTP OPTIONS
request sent by a web browser to a target server before executing the
actual network request. It acts as a safety check to determine whether
the destination server understands the CORS protocol and grants
permission for the client’s origin domain to perform the requested
operation with specific methods and headers.
If the server grants permission by returning appropriate CORS
response headers with a successful HTTP status code (typically
200 OK or 204 No Content), the browser
proceeds with the original fetch call. If the server fails
to respond or omits the necessary CORS headers, the browser blocks the
main request and throws a CORS network error in the JavaScript
console.
Why Browsers Send Preflight Requests
Before CORS was standardized, cross-origin requests were heavily restricted to simple forms and image tags to prevent malicious websites from executing unauthorized operations against protected servers.
When modern JavaScript APIs like fetch and
XMLHttpRequest enabled sending arbitrary HTTP methods (such
as DELETE or PATCH) and custom headers (such
as Authorization), browsers needed a mechanism to prevent
potential side effects on legacy servers not designed to handle these
requests. The preflight check ensures the server explicitly consents to
the operation before any modifying payload reaches the server’s endpoint
logic.
Conditions That Trigger a Preflight Request
A browser automatically initiates a preflight OPTIONS
request if the fetch call does not qualify as a “simple
request.” A request requires a preflight check if it meets any of the
following conditions:
- HTTP Methods: It uses an HTTP method other than
GET,POST, orHEAD(such asPUT,DELETE,PATCH). - Non-Standard Headers: It includes custom headers,
or any header other than standard safe-listed headers like
Accept,Accept-Language,Content-Language, orContent-Type. - Complex Content-Type: The
Content-Typeheader is set to anything other thanapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain. SettingContent-Type: application/jsonis the most common cause of preflight requests. - ReadableStreams: The request body uses a
ReadableStreaminstance.
The Preflight Request and Response Headers
During the preflight phase, the browser automatically attaches
specific metadata headers to the OPTIONS request:
Origin: The origin (protocol, domain, port) of the calling script.Access-Control-Request-Method: The HTTP method of the pending request (e.g.,PUT,DELETE).Access-Control-Request-Headers: A comma-separated list of any custom headers attached to the pending request (e.g.,authorization,content-type).
To successfully fulfill the preflight check, the server must respond with matching authorization headers:
Access-Control-Allow-Origin: Specifies which origins are permitted (e.g.,https://example.comor*).Access-Control-Allow-Methods: Lists the permitted HTTP methods (e.g.,GET, POST, PUT, DELETE, OPTIONS).Access-Control-Allow-Headers: Lists the permitted request headers.Access-Control-Max-Age: Defines the number of seconds the preflight response can be cached by the browser, preventing redundantOPTIONSrequests for subsequent calls.
Optimizing Preflight Performance
Because preflight requests introduce an additional network round-trip that increases latency, applications can optimize them using the following techniques:
- Leverage the
Access-Control-Max-AgeHeader: Configure the server to return anAccess-Control-Max-Ageheader (e.g.,86400for 24 hours). The browser will cache the permission and skip preflight checks for identical requests during that window. - Use Simple Requests Where Feasible: When low latency is critical and advanced security headers or JSON bodies are not strictly necessary, structuring payloads to use standard form encodings allows requests to bypass the preflight phase entirely.