Glossary Term
Preflight Request (CORS OPTIONS)
A CORS preflight is an automatic OPTIONS request the browser sends before a cross-origin request to check permission. See when it fires and how to answer it.
TL;DR: A preflight is an
OPTIONSrequest the browser sends by itself before a cross-origin request that could have side effects, to ask the server whether it allows that method and those headers.
A CORS preflight request is an automatic OPTIONS request that a browser sends to a cross-origin URL before the actual request, when that request is not a “simple” one. The server’s answer tells the browser whether to send the real request at all. Your JavaScript never sees the preflight and cannot add headers to it.
What triggers one
You get a preflight when a cross-origin request uses a method other than GET, HEAD or POST, or sets a header outside the CORS-safelisted set (Accept, Accept-Language, Content-Language, Content-Type limited to three values, and Range in simple form). The two triggers people hit most are:
Authorizationheader on any requestContent-Type: application/json, because onlyapplication/x-www-form-urlencoded,multipart/form-dataandtext/plainare safelisted
The exchange
OPTIONS /v1/orders HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, PUT, DELETE
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Max-Age: 600
Vary: Origin
If the answer does not cover the method and headers, Chrome logs Response to preflight request doesn't pass access control check and the real request is never sent.
Non-obvious facts
- The preflight must succeed with an ok status. A 401, 403, 404 or 500 on the
OPTIONSrequest fails CORS even if yourPUThandler is fine. Auth middleware that runs first is the usual culprit, because the preflight carries no cookies orAuthorizationheader. Access-Control-Max-Ageis capped by the browser. The default is 5 seconds, so without it every cross-origin call becomes two round trips. Chromium caps the value at 2 hours (7200) and Firefox at 24 hours.- Add
Vary: Originwhen you echo the origin. Otherwise a CDN may cache one origin’s preflight and serve it to another. - Credentials need exact values. With
Access-Control-Allow-Credentials: true, the wildcard*is not accepted for origin, headers or methods. - Redirects on a preflight fail. The preflight response must not be a redirect, so a trailing-slash 301 on the API path breaks it.
Go deeper
Frequently asked questions
What is a CORS preflight request?
It is an OPTIONS request the browser sends on its own, before the real request, to ask the target server whether the cross-origin method and headers are allowed.
When does the browser send a preflight?
When the request is not a simple request: a method other than GET, HEAD or POST, a non-safelisted header such as Authorization or a JSON Content-Type, or a few other conditions defined by the Fetch Standard.
Why does my preflight fail with a 401 or 404?
Preflights never carry credentials, so authentication middleware that runs before your CORS handler rejects them. Answer OPTIONS before auth, with a 2xx status.
How long is a preflight response cached?
As long as Access-Control-Max-Age says, capped by the browser. The default is 5 seconds, and Chromium caps it at 2 hours while Firefox allows up to 24 hours.
Sources
Related
Cross-Origin Resource Sharing (CORS)
Master Cross-Origin Resource Sharing (CORS) for secure cross-origin HTTP requests. Learn preflight requests, headers, credentials, and common error solutions.
Access-Control-Max-Age Header
Learn how Access-Control-Max-Age specifies how long browsers can cache CORS preflight results. Reduce preflight requests and improve cross-origin performance.
HTTP OPTIONS Method
Learn how HTTP OPTIONS requests discover server capabilities, supported methods, and handle CORS preflight checks for cross-origin requests.
No 'Access-Control-Allow-Origin' Header Is Present: Fix CORS
Fix the CORS error 'No Access-Control-Allow-Origin header is present on the requested resource' with working Express, nginx, S3, Workers and Django fixes.