Before sending certain cross-origin requests, the browser first sends an OPTIONS request called a preflight to ask the server whether the real request is allowed. If that preflight fails, the real request is never sent, and the console shows something like:
Access to fetch at 'https://api.example.com/items' from origin 'https://app.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Variants include Redirect is not allowed for a preflight request, and Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response. This page is about the preflight itself. If the problem is a missing Access-Control-Allow-Origin on the normal response, read CORS: no Access-Control-Allow-Origin header.
When a preflight happens
Browsers preflight any request that is not "simple". Common triggers:
- A method other than GET, HEAD or POST (PUT, PATCH, DELETE).
- A custom header such as
AuthorizationorX-Api-Key. - A
Content-Typeother thanapplication/x-www-form-urlencoded,multipart/form-dataortext/plain, so JSON bodies (application/json) trigger it.
That is why a plain GET works while the same endpoint fails once you add a token header.
Step 1: send the preflight yourself
Reproduce it with curl, copying the origin, method and headers from the browser's Network tab:
curl -i -X OPTIONS https://api.example.com/items \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: authorization,content-type"
A healthy answer is a 2xx status (200 or 204) with headers like these:
HTTP/2 204
access-control-allow-origin: https://app.example.com
access-control-allow-methods: GET, POST, PUT, DELETE, OPTIONS
access-control-allow-headers: authorization, content-type
access-control-max-age: 600
The CORS Checker runs a similar test, and the HTTP Header Checker shows the headers of a response. The status must be in the 200 range for the browser to accept it.
Step 2: match the failure to a cause
| What the OPTIONS request returns | Likely cause |
|---|---|
| 404 or 405 | The route or framework does not handle OPTIONS |
| 401 or 403 | Authentication runs before CORS and rejects the credential-less preflight |
| 301, 302, 307 or 308 | An HTTP-to-HTTPS, trailing-slash or www redirect on the API URL |
| 200 with no CORS headers | CORS middleware not applied to this route or proxy path |
| 5xx | The app crashes on OPTIONS, or the proxy cannot reach it |
The key point behind the 401 row: browsers send preflights without credentials, so a handler that requires a token on every request, OPTIONS included, will always fail.
Step 3: fix it at the right layer
Order of middleware. Put CORS handling before authentication, so it can answer OPTIONS first. In Express with the cors package:
const cors = require('cors');
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET','POST','PUT','DELETE'],
allowedHeaders: ['Authorization','Content-Type'],
maxAge: 600,
}));
// routes and auth middleware come after this line
Nginx in front of an app. Answer preflights directly and add headers to real responses:
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
add_header Access-Control-Max-Age 600 always;
return 204;
}
proxy_pass http://backend;
}
Redirects. Browsers do not follow redirects for preflights. Make the front-end call the final canonical URL (correct scheme, host and trailing slash). Test with the रीडायरेक्ट चेकर.
CDN, gateway and WAF. A firewall rule or API gateway may block OPTIONS or strip headers. Find which layer answered with the CDN डिटेक्टर and the response server or via headers. Many API gateways have their own CORS switch, so check your platform's current documentation.
Pitfalls
- Wildcards with credentials. If requests send cookies or
credentials: 'include',Access-Control-Allow-Origincannot be*; use the exact origin and addAccess-Control-Allow-Credentials: true. Wildcards are not allowed for allowed headers in credentialed requests either. - Reflecting any Origin. Echoing back whatever origin arrives, with credentials enabled, effectively disables the protection. Check against an allow list.
- Vary header. If the allowed origin varies per request, send
Vary: Originso caches do not serve one origin's answer to another. - Caching.
Access-Control-Max-Agelets browsers reuse a preflight result, but browsers cap the value, so do not rely on large numbers.
Verify
Reload with the browser cache disabled in DevTools, confirm the OPTIONS row shows 200 or 204 and the real request follows it. Keep the API endpoint under an अपटाइम मॉनिटर, and confirm status codes with the HTTP स्टेटस चेकर.
अगले कदम
- Read CORS: no Access-Control-Allow-Origin.
- Learn the header basics in HTTP security headers.
- Monitor APIs using how to monitor API endpoints.
- Look up HTTP 405 if OPTIONS is rejected.
सामान्य प्रश्न
Why does the browser send an OPTIONS request?
It is the preflight, a permission check the browser performs before a cross-origin request that could have side effects. The server must answer it correctly before the real request goes out.
Why does the preflight fail when my API works in Postman?
Postman does not enforce CORS and does not send preflights. Browsers do, and they require a 2xx response with the right headers.
Can I skip preflights?
Only by keeping requests "simple", which usually means avoiding custom headers and JSON content types. Serving the API from the same origin (a reverse proxy) removes CORS altogether.
Does a 204 count as OK?
Yes. Any 2xx status satisfies the browser's check; 204 No Content is common for preflights.
Get notified when this fix updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.