A CORS respecting client will issue an OPTIONS request before the actual request, in order to observe the Access-Control-* headers returned.
If I want a client to be able to POST an endpoint, is it sufficient with
Access-Control-Allow-Methods: HEAD, GET, POST
Or do I also have to explicitly allow OPTIONS
Access-Control-Allow-Methods: OPTIONS, HEAD, GET, POST
My answer: no, and I've got 3 angles to argue from.
First, common sense. The OPTIONS HTTP method is the "API" for CORS authorization: the server permits or denies a particular cross-origin request in response to the corresponding OPTIONS "preflight" request (if one is needed¹). See MDN CORS overview.
Now, a preflight with Access-Control-Request-Method: OPTIONS is nonsensical. That's because if OPTIONS requests had to be explicitly authorized by the server — the UA (User-Agent; the browser) would have no way to obtain that authorization. Because the way to obtain it is making an OPTIONS request. Observe the recursive self-dependency: the UA can only make an OPTIONS request... to obtain permission to make OPTIONS requests. That makes no sense.
In practice, supporting UAs well understand the semantics of CORS and don't require any permission to send OPTIONS preflights.
Second, read the spec. The W3 standard mandates the resource sharing check only at these points:
Crucially, preflight request is not actual request; and the rules of processing them do not mention the resource sharing check. The UA is not obliged to check permissions when sending preflights.
Third, read the source. To avoid too theoretic style of pitfalls — check a real browser-engine implementation. Like Chromium.
Here, if you read C++, the function CheckAccessInternal() in cors.cc does all sorts of checks, along the lines of the resource sharing check from the standard. Also see unit tests.
However, preflight requests are not ye olde ajax; they're constructed by the browser internally; sent, checked and handled in another place: cors_url_loader.cc. There you can see, no calls to CheckAccessInternal() or anything like that.
This is to say that a preflight request with Access-Control-Request-Method: OPTIONS makes no sense; likewise, the response header Access-Control-Allow-Methods: OPTIONS has no effect (unless of course you reuse the OPTIONS method for something else in your webapp).
¹ preflights are skipped with simple cross-origin requests and on preflight result cache hit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With