The Fetch Specifications say that the default Fetch mode is 'no-cors' -
A request has an associated mode, which is "same-origin", "cors", "no-cors", "navigate", or "websocket". Unless stated otherwise, it is "no-cors".
But, I seem to be noticing this behavioral difference between mode: 'no-cors'
and an unspecified mode. As demonstrated in this JSFiddle sample, explicitly defining mode as 'no-cors' makes the response inaccessible to the Javascript, while not specifying a mode makes the Response object available to the calling method.
Does explicitly specifying the fetch mode work differently from the default behavior that internally uses the same mode? What am I missing here?
no-cors
is only the default for the base Fetch algorithm. But in various other places in the spec, that algorithm gets called into with a specific mode stated other than no-cors
. In particular, for cross-origin requests, the algorithm gets called with the mode set to cors
.
More specifically, for cross-origin requests, the Fetch algorithm sets the “response tainting” for the request to cors
and requires the fetch to be performed using the CORS protocol.
And even more specifically, see the following substeps of step 12 of the “Main fetch” algorithm:
↪ request’s current url’s origin is same origin with request’s origin and CORS flag is unset
↪ request’s current url’s scheme is "data
"
↪ request’s mode is "navigate
" or "websocket
"
- Set request’s response tainting to "
basic
".- Return the result of performing a scheme fetch using request.
…↪ request’s mode is "
no-cors
"
- Set request’s response tainting to "
opaque
".- Return the result of performing a scheme fetch using request.
…↪ Otherwise
- Set request’s response tainting to "
cors
".- Return the result of performing an HTTP fetch using request with CORS flag set.
What it amounts to is, if the request URL isn’t same-origin with your code’s origin, the scheme isn’t data
, and the mode isn’t navigate
or websocket
or isn’t explicitly set to no-cors
, then that Otherwise condition gets reached, and the request gets made using the CORS protocol.
That explanation simplifies things a bit because in the case where the request has properties that trigger the browser to do a preflight, there’s a substep just above that Otherwise substep that gets reached instead—but again in that case, the “response tainting” gets set to cors
.
But, I seem to be noticing this behavioral difference between
mode: 'no-cors'
and an unspecified mode.
The difference is because when you explicitly set no-cors
mode for the request, in that case the “↪ request’s mode is "no-cors"” substep gets reached instead of the Otherwise substep.
As demonstrated in this JSFiddle sample, explicitly defining mode as 'no-cors' makes the response inaccessible to the Javascript,
Right—that’s because it causes the “↪ request’s mode is "no-cors"” substep in the “Main fetch” algorithm to get reached instead of the Otherwise substep.
while not specifying a mode makes the Response object available to the calling method.
Because the Otherwise substep does then get reached, so a CORS-enabled fetch gets made.
Does explicitly specifying the fetch mode work differently from the default behavior that internally uses the same mode?
Yes, for a cross-origin request, explicitly setting mode: 'no-cors'
forces the request to be made without using CORS, while leaving it unspecified causes it to be made using CORS.
That “Unless stated otherwise, it is "no-cors
"” statement about the mode definitely isn’t meant to specify that requests that get made using the fetch(…)
method without an explicit mode specified end up having their mode locked into no-cors
.