I can hit this endpoint, http://catfacts-api.appspot.com/api/facts?number=99
via Postman and it returns JSON
Additionally I am using create-react-app and would like to avoid setting up any server config.
In my client code I am trying to use fetch
to do the same thing, but I get the error:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:3000' is therefore not allowed
access. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
So I am trying to pass in an object, to my Fetch which will disable CORS, like so:
fetch('http://catfacts-api.appspot.com/api/facts?number=99', { mode: 'no-cors'})
.then(blob => blob.json())
.then(data => {
console.table(data);
return data;
})
.catch(e => {
console.log(e);
return e;
});
Interestingly enough the error I get is actually a syntax error with this function. I am not sure my actual fetch
is broken, because when I remove the { mode: 'no-cors' } object, and supply it with a different URL it works just fine.
I have also tried to pass in the object { mode: 'opaque'}
, but this returns the original error from above.
I belive all I need to do is disable CORS.. What am I missing?
Best Answer
mode: 'no-cors'
won’t magically make things work. In fact it makes things worse, because one effect it has is to tell browsers, “Block my frontend JavaScript code from seeing contents of the response body and headers under all circumstances.” Of course you never want that.What happens with cross-origin requests from frontend JavaScript is that browsers by default block frontend code from accessing resources cross-origin. If
Access-Control-Allow-Origin
is in a response, then browsers relax that blocking and allow your code to access the response.But if a site sends no
Access-Control-Allow-Origin
in its responses, your frontend code can’t directly access responses from that site. In particular, you can’t fix it by specifyingmode: 'no-cors'
(in fact that’ll ensure your frontend code can’t access the response contents).However, one thing that will work: if you send your request through a CORS proxy.
You can also easily deploy your own proxy to Heroku in just 2-3 minutes, with 5 commands:
After running those commands, you’ll end up with your own CORS Anywhere server running at, for example,
https://cryptic-headland-94862.herokuapp.com/
.Prefix your request URL with your proxy URL; for example:
Adding the proxy URL as a prefix causes the request to get made through your proxy, which:
https://example.com
.https://example.com
.Access-Control-Allow-Origin
header to the response.The browser then allows the frontend code to access the response, because that response with the
Access-Control-Allow-Origin
response header is what the browser sees.This works even if the request is one that triggers browsers to do a CORS preflight
OPTIONS
request, because in that case, the proxy also sends back theAccess-Control-Allow-Headers
andAccess-Control-Allow-Methods
headers needed to make the preflight successful.https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explains why it is that even though you can access the response with Postman, browsers won’t let you access the response cross-origin from frontend JavaScript code running in a web app unless the response includes an
Access-Control-Allow-Origin
response header.http://catfacts-api.appspot.com/api/facts?number=99 has no
Access-Control-Allow-Origin
response header, so there’s no way your frontend code can access the response cross-origin.Your browser can get the response fine and you can see it in Postman and even in browser devtools—but that doesn’t mean browsers expose it to your code. They won’t, because it has no
Access-Control-Allow-Origin
response header. So you must instead use a proxy to get it.The proxy makes the request to that site, gets the response, adds the
Access-Control-Allow-Origin
response header and any other CORS headers needed, then passes that back to your requesting code. And that response with theAccess-Control-Allow-Origin
header added is what the browser sees, so the browser lets your frontend code actually access the response.You don’t want to do that. To be clear, when you say you want to “disable CORS” it seems you actually mean you want to disable the same-origin policy. CORS itself is actually a way to do that — CORS is a way to loosen the same-origin policy, not a way to restrict it.
But anyway, it’s true you can—in your local environment—do suff like give a browser runtime flags to disable security and run insecurely, or you can install a browser extension locally to get around the same-origin policy, but all that does is change the situation just for you locally.
No matter what you change locally, anybody else trying to use your app is still going to run into the same-origin policy, and there’s no way you can disable that for other users of your app.
You most likely never want to use
mode: 'no-cors'
in practice except in a few limited cases, and even then only if you know exactly what you’re doing and what the effects are. That’s because what settingmode: 'no-cors'
actually says to the browser is, “Block my frontend JavaScript code from looking into the contents of the response body and headers under all circumstances.” In most cases that’s obviously really not what you want.As far as the cases when you would want to consider using
mode: 'no-cors'
, see the answer at What limitations apply to opaque responses? for the details. The gist of it is:In the limited case when you’re using JavaScript to put content from another origin into a
<script>
,<link rel=stylesheet>
,<img>
,<video>
,<audio>
,<object>
,<embed>
, or<iframe>
element (which works because embedding of resources cross-origin is allowed for those)—but for some reason you don’t want to/can’t do that just by having the markup of the document use the resource URL as thehref
orsrc
attribute for the element.When the only thing you want to do with a resource is to cache it. As alluded to in What limitations apply to opaque responses?, in practice the scenario that’s for is when you’re using Service Workers, in which case the API that’s relevant is the Cache Storage API.
But even in those limited cases, there are some important gotchas to be aware of; see the answer at What limitations apply to opaque responses? for the details.
There is no
mode: 'opaque'
request mode—opaque
is instead just a property of the response, and browsers set that opaque property on responses from requests sent withno-cors
mode.But incidentally the word opaque is a pretty explicit signal about the nature of the response you end up with: “opaque” means you can’t see it.