I think I understand CORS pretty well, but I'm still a bit puzzled about the browser's behavior when it comes to the preflight requests.
Let's say the browser issues this preflight request:
OPTIONS http://myserver.local:7000/api/order/4 HTTP/1.1
Host: myserver.local:7000
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-my-custom-header
Origin: http://localhost:5000
Sec-Fetch-Mode: cors
Referer: http://localhost:5000/
and my API returns:
HTTP/1.1 204 No Content
Date: Wed, 09 Mar 2022 12:52:50 GMT
Server: Kestrel
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT,DELETE
Access-Control-Allow-Origin: http://localhost:5000
Vary: Origin
Note that the server allows methods PUT
and DELETE
in the response to the preflight request, but not POST
, which is the method of the actual CORS request.
Should the browser not block this request due to the mismatch between the actual request's method and the methods listed in the Access-Control-Allow-Methods
header? Or is it enough that the server respond with a 20x
status code for the browser to accept the preflight and then send the actual request?
My assumptions was that the browser would compare the allow-methods and block the request if the requested method did no match... Am I missing something?
No, the browser doesn't require the server to explicitly allow the POST
method, because the latter, as a so-called CORS-safelisted method, gets a free pass.
The answer, as always, lies in the Fetch standard (section 4.8), which specifies how CORS works:
- Let methods be the result of extracting header list values given
Access-Control-Allow-Methods
and response’s header list.
And further down:
- If request’s method is not in methods, request’s method is not a CORS-safelisted method, and request’s credentials mode is
"include"
or methods does not contain*
, then return a network error.
(my emphasis)
What is a CORS-safelisted method? The term is defined in section 2.2.1:
A CORS-safelisted method is a method that is
GET
,HEAD
, orPOST
.
If the method of the CORS request is one of GET
, HEAD
, or POST
, the browser doesn't require the server to explicitly list that method in the Access-Control-Allow-Methods
header for CORS preflight to succeed.
I've found Jake Archibald's CORS playground useful for testing my (mis)understanding of CORS. Running this particular instance in your browser may convince you that the POST
method doesn't need to be explicitly allowed for CORS preflight to succeed.