/errors/cors/preflight-does-not-have-http-ok-status
Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
- tool
- CORS
- versions
- Chrome, Edge, other Chromium browsers
- os
- any
Response to preflight request doesn't pass access control check: It does not have HTTP ok status. means the browser sent a CORS preflight (OPTIONS) before your real request, and the server answered it with a status outside 200-299: usually 401 or 403 from authentication middleware, or 404 or 405 because nothing handles OPTIONS. The browser then never sends the real request. Make the server answer OPTIONS on that path with 204 and the CORS headers, without requiring authentication.
What the browser does
For a cross-origin request that is not "simple" (a JSON body, an Authorization header, methods like PUT or DELETE), the browser first sends:
OPTIONS /api/items HTTP/1.1
Origin: http://localhost:5173
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-typeThe Fetch standard requires an "ok status" (200-299) for this response. The preflight never includes cookies or the Authorization header, so any middleware that demands them rejects it. Chrome prints the full message as Access to fetch at 'URL' from origin 'ORIGIN' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
See the status the server returns
A local API where authentication runs before CORS: the preflight gets 401. Then the same API with the order fixed:
cat > api.js <<'EOF'
const http = require("http");
const corsFirst = process.argv[2] === "fixed";
http.createServer((req, res) => {
const cors = () => {
res.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
res.setHeader("Access-Control-Allow-Methods", "GET, PUT, DELETE");
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
};
if (corsFirst) {
cors();
if (req.method === "OPTIONS") { res.statusCode = 204; return res.end(); }
}
if (!req.headers.authorization) { res.statusCode = 401; return res.end("unauthorized\n"); }
cors();
res.end("ok\n");
}).listen(4928, "127.0.0.1");
setTimeout(() => process.exit(0), 3000);
EOF
preflight() {
curl -s -o /dev/null -w '%{http_code}\n' -X OPTIONS http://127.0.0.1:4928/api/items \
-H 'Origin: http://localhost:5173' \
-H 'Access-Control-Request-Method: PUT' \
-H 'Access-Control-Request-Headers: authorization, content-type'
}
node api.js &
sleep 1
echo "auth before CORS: $(preflight)"
wait
node api.js fixed &
sleep 1
echo "CORS before auth: $(preflight)"
waitOutput
auth before CORS: 401 CORS before auth: 204
Any status that is not 2xx gives this error in the browser, even if the response carries every Access-Control-Allow-* header.
Fix by stack
- Express: register
app.use(cors({...}))before your auth middleware; thecorspackage answers preflights with204. - Spring Security: enable CORS in the security chain (
http.cors(Customizer.withDefaults())) so the CORS filter runs before authentication. - ASP.NET Core: call
app.UseCors()beforeapp.UseAuthentication()andapp.UseAuthorization(). - nginx in front of the API: answer
if ($request_method = OPTIONS) { return 204; }with the CORS headers addedalways, or pass OPTIONS to the backend.
A redirect on the preflight (for example /api/items to /api/items/, or HTTP to HTTPS) also fails, because the preflight must be answered directly. Call the final URL.