Skip to content
MtrXTools

CORS tester

Send the preflight a browser would send from an origin you choose, then check Access-Control-Allow-Origin, methods, headers, credentials, Vary and Max-Age the way the browser does, with the reason a request is blocked and a curl command to repeat it.

What it checks

  • Whether the browser needs a preflight at all: it does for methods other than GET, HEAD and POST, and for requests that set headers such as Authorization or a JSON Content-Type.
  • The preflight (an OPTIONS request): a 2xx status with no redirect, Access-Control-Allow-Origin matching your origin, the method in Access-Control-Allow-Methods, every header in Access-Control-Allow-Headers, and how long Access-Control-Max-Age lets the browser cache it.
  • The response itself: exactly one allowed origin (or *), Access-Control-Allow-Credentials: true when cookies are sent, Vary: Origin when the origin is echoed, and which response headers scripts can read.
  • The rules that trip people up: * never works with credentials, the wildcard in Access-Control-Allow-Headers never covers Authorization, origins and method names are compared exactly, and allowing the null origin lets any site in.

Only the preflight and a GET are ever sent. A POST, PUT, PATCH or DELETE could change data, so for those the response checks use a GET to the same URL with the same Origin.

Reading a CORS error

The browser makes the request either way; CORS only decides whether the page's script may read the response. That is why the console shows "blocked by CORS policy" even though the server logged the request, and why a tool like curl, which has no same-origin policy, works fine.

Fix CORS on the server that owns the API, not in the page: answer OPTIONS before authentication and routing, echo back an allowed origin from a list rather than any origin, and add Vary: Origin so caches keep each origin's answer apart.

Questions

Why does my request work in curl but not in the browser?
curl doesn't enforce the same-origin policy, so it never sends a preflight or checks the CORS headers. The browser does both, and blocks the page from reading the response when the headers don't allow its origin.
Can I use Access-Control-Allow-Origin: * with cookies?
No. With credentials the server must name the exact origin and send Access-Control-Allow-Credentials: true. To allow several sites, check the request's Origin against your list and echo back the one that matches.
Why does the preflight fail with 401 or 405?
Preflights carry no cookies or Authorization header, and many frameworks don't route OPTIONS. The CORS handling has to answer OPTIONS requests itself, before authentication and before the router returns 404 or 405.
Is reflecting the Origin header safe?
Only if you check it against a list first. A server that echoes any origin with credentials allowed lets every website read your signed-in users' data.