GuidesDIAGNOSE

Response to preflight request doesn't pass access control check

When Chrome logs Response to preflight request doesn't pass access control check, the browser sent an OPTIONS probe before your actual request and rejected the answer. The text after the final colon isolates the exact reason the preflight failed.

The exact console message

Chrome console
Access to fetch at 'https://api.example.com/data' from origin 'https://your-site.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Chrome prints this generic wrapper whenever an OPTIONS preflight request fails its validation checks. The text after the final colon provides the real diagnosis for why the browser rejected the response.

What the browser is telling you

Your browser determined that the planned HTTP request required a preflight check, typically because it uses non-safelisted methods or headers such as Content-Type: application/json. Before dispatching the actual request, the browser issued an automatic OPTIONS request to verify that the server permits the cross-origin call.

The server's response to that OPTIONS probe failed one of the browser's CORS rules. Because the preflight failed, the browser terminated the request sequence immediately. The actual GET, POST, or PUT request was never transmitted to the server. You can inspect the failed OPTIONS transaction directly inside your browser's Network panel.

Decode the trailing clause

Match the specific clause at the end of the console error to find the exact misconfiguration on your server.

Preflight error clauses and their solutions
Console clauseCauseFix
Header missingChrome prints: No 'Access-Control-Allow-Origin' header is present on the requested resource.The server answered OPTIONS without CORS headers. Add Access-Control-Allow-Origin to all preflight responses.
Preflight not ok statusChrome prints: It does not have HTTP ok status.The OPTIONS response was non-2xx (401/403/404/5xx). Exempt OPTIONS from auth middleware, handle the route, return 200 or 204.
Preflight hit a redirectChrome prints: Redirect is not allowed for a preflight request.The URL answered 3xx (http to https, apex to www, trailing slash). Point the client at the final URL directly.
Wildcard with credentialsChrome prints: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.A credentialed request met a wildcard. Echo the exact request Origin instead of *.
Wrong origin valueChrome prints: The 'Access-Control-Allow-Origin' header has a value '<value>' that is not equal to the supplied origin.The preflight echoed a fixed origin. Match the incoming Origin header for allowed origins.
Credentials header missingChrome prints: The value of the 'Access-Control-Allow-Credentials' header in the response is '<value>' which must be 'true' when the request's credentials mode is 'include'.A credentialed request lacked the credentials header. Send Access-Control-Allow-Credentials: true on the preflight.
Method not allowed (no wrapper)Chrome prints: Method <METHOD> is not allowed by Access-Control-Allow-Methods in preflight response.The allow list omitted the method. Add it to Access-Control-Allow-Methods.
Header field not allowed (no wrapper)Chrome prints: Request header field <name> is not allowed by Access-Control-Allow-Headers in preflight response.The allow list omitted a custom header the client sends. Add it to Access-Control-Allow-Headers.

The rows marked no wrapper print directly after blocked by CORS policy: instead of inside the preflight wrapper. They mean the preflight answered 2xx with CORS headers, but the allow list omitted the method or header your request needs.

Fix the preflight response

The most frequent production cause is authentication middleware running ahead of CORS handling. Because browsers never send credentials such as cookies or the Authorization header on an OPTIONS preflight, auth middleware rejects the probe with status 401 or 403. Apply these changes to correct your preflight behavior.

  1. Reorder server middleware

    Place your CORS handling middleware before authentication and authorization middleware so OPTIONS requests receive preflight headers before any auth checks run.

  2. Exempt OPTIONS from auth checks

    Ensure your authentication handlers immediately skip requests that use the OPTIONS method.

  3. Return an HTTP 2xx status

    Answer valid OPTIONS requests with status 204 No Content or status 200 OK along with your CORS allow headers.

  4. Eliminate preflight redirects

    Verify that your client points to the canonical API endpoint, avoiding HTTP to HTTPS upgrades, apex to www changes, or missing trailing slashes that trigger 3xx redirects.

  5. Configure origins and credentials accurately

    When sending cookies or authorization headers from the browser, echo the exact requesting origin in Access-Control-Allow-Origin and supply Access-Control-Allow-Credentials: true.

  6. Expand allowed methods and headers

    Populate Access-Control-Allow-Methods and Access-Control-Allow-Headers to cover every method and custom header sent by the client application.

Express: CORS before auth
import cors from 'cors';
import express from 'express';

const app = express();

// CORS runs first and answers OPTIONS with a 204 before auth sees it.
app.use(
  cors({
    origin: 'https://your-site.com',
    credentials: true,
  }),
);

// Preflights carry no cookies or Authorization header, so any auth
// middleware mounted above the CORS layer 401s every OPTIONS request.
app.use(requireAuth);

app.post('/data', (req, res) => {
  res.json({ ok: true });
});

Verify the fix

Emulate the browser preflight call using curl. Include an Origin header and the Access-Control-Request-Method header to confirm that the server responds with a 2xx status and the required Access-Control-Allow-* headers.

Replay the preflight by hand
$ curl -i https://api.example.com/data \
    -X OPTIONS \
    -H "Origin: https://your-site.com" \
    -H "Access-Control-Request-Method: POST" \
    -H "Access-Control-Request-Headers: content-type"

HTTP/2 204
access-control-allow-origin: https://your-site.com
access-control-allow-methods: POST
access-control-allow-headers: content-type

# The browser needs exactly this: a 2xx status plus the allow headers.

Good questions.

Why does my preflight request arrive without an Authorization header or cookies?

By browser design, preflight OPTIONS requests never transmit user credentials such as cookies or the Authorization header. Any authentication middleware that requires credentials will consistently reject preflights if placed before the CORS handler.

Does returning an HTTP 200 OK on OPTIONS satisfy the check?

No. The preflight check requires both a 2xx status code and the appropriate Access-Control-Allow-* response headers. Returning status 200 without Access-Control-Allow-Origin still triggers an error.

Why does a GET request succeed while a POST request fails with a preflight error?

A simple GET request with standard headers does not trigger a preflight request. When you send a POST request with Content-Type: application/json, the browser identifies it as a non-safelisted content type and sends an OPTIONS preflight first.

Can a CORS proxy resolve preflight failures on third-party APIs?

If you are calling a third-party public API that you cannot modify, routing anonymous GET and HEAD requests through cors.dev supplies the required headers for any public host. Credentials are never forwarded, so the proxy serves public data only; an API you control should be fixed at its own server.