Trying to use fetch and pass in mode: no-cors
Asked Answered
P

12

505

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?

Philippine answered 6/4, 2017 at 17:36 Comment(1)
Does this answer your question? Handle response - SyntaxError: Unexpected end of input when using mode: 'no-cors'Idell
G
782

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 specifying mode: '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:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

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:

https://cryptic-headland-94862.herokuapp.com/https://example.com

Adding the proxy URL as a prefix causes the request to get made through your proxy, which:

  1. Forwards the request to https://example.com.
  2. Receives the response from https://example.com.
  3. Adds the Access-Control-Allow-Origin header to the response.
  4. Passes that response, with that added header, back to the requesting frontend code.

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 the Access-Control-Allow-Headers and Access-Control-Allow-Methods headers needed to make the preflight successful.


I can hit this endpoint, http://catfacts-api.appspot.com/api/facts?number=99 via Postman

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 the Access-Control-Allow-Origin header added is what the browser sees, so the browser lets your frontend code actually access the response.


So I am trying to pass in an object, to my Fetch which will disable CORS

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 setting mode: '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 the href or src 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.


I have also tried to pass in the object { mode: 'opaque'}

There is no 'opaque' request mode — opaque is instead just a property of the response, and browsers set that opaque property on responses from requests sent with no-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 into any of its details; it blocks you from seeing.

Gradient answered 7/4, 2017 at 1:8 Comment(6)
I needed to configure my Express server with the CORS.js package - github.com/expressjs/cors - and then I needed to remove mode: 'no-cors' from the fetch request (otherwise the response would be empty)Maishamaisie
This is a really stupid error message from Chrome: Access to fetch at 'quake.okayfun.com/maps/baseq3/index.json' from origin 'localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.Swage
Instead of running a local Heroku App, you can prepend the general url http://cors-anywhere.herokuapp.comEnzootic
one case where you want to use no-cors is a heartbeat from an SPA to a monitoring service that alerts when they have not received a call in a set time. You do no care about the response: either it wen through and it is fine, or it did not and the monitoring service will raise an alertOrator
I've tried this Heroku cors option, but still end up with a 403 Forbidden error. Anyone can advise? I'm using expo react native app but need to do OIDC to integrate our company's identity/SSOSliver
@Orator omg, I have a similar case. I need to send an email address to a mailing list and I don't care of a response. I am not the only one who can see a use case. It really bugs me that the most voted answer says ". 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.". That statement on it's own is not great. There are uses cases like ours where no-cors makes things work well.Payoff
M
26

If you are trying to address this issue temporarily on your localhost, you can use this Chrome extension :

Allow CORS Access-Control-Allow-Origin

Masuria answered 11/11, 2021 at 17:15 Comment(0)
A
16

If you are using Express as back-end you just have to install cors and import and use it in

app.use(cors());.

If it is not resolved then try switching ports. It will surely resolve after switching ports

Arluene answered 23/8, 2020 at 13:29 Comment(3)
I remember my senior year college class & no other groups got this working except ours--Oddly it's a simple solution. Here's a more thorough guide: stackabuse.com/handling-cors-with-node-jsNathan
this is only for if youre requesting from your own backend right? Not from some other API without the Access-Control-Allow-Origin header set? Or do you mean to use a express server as a proxy?Opec
The switch port action works like a charm!Runnerup
E
11

So if you're like me and developing a website on localhost where you're trying to fetch data from Laravel API and use it in your Vue front-end, and you see this problem, here is how I solved it:

  1. In your Laravel project, run command php artisan make:middleware Cors. This will create app/Http/Middleware/Cors.php for you.
  2. Add the following code inside the handles function in Cors.php:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    
  3. In app/Http/kernel.php, add the following entry in $routeMiddleware array:

    ‘cors’ => \App\Http\Middleware\Cors::class
    

    (There would be other entries in the array like auth, guest etc. Also make sure you're doing this in app/Http/kernel.php because there is another kernel.php too in Laravel)

  4. Add this middleware on route registration for all the routes where you want to allow access, like this:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
    
  5. In Vue front-end, make sure you call this API in mounted() function and not in data(). Also make sure you use http:// or https:// with the URL in your fetch() call.

Full credits to Pete Houston's blog article.

Euhemerus answered 17/7, 2019 at 20:18 Comment(1)
I had the same problem, and I was using express.js as a backEnd and react.js as a frontEnd on localhost with different ports, so after I saw your answer tried to fine semileler thing on node and found Express cors middleware and it worked fine.Witenagemot
J
9

You can also set up a reverse proxy which adds the CORS headers using a self-hosted CORS Anywhere or Just CORS if you want a managed solution.

https://justcors.com/<id>/<your-requested-resource>
http://cors-anywhere.com/<your-requested-resource>
Justiciary answered 23/11, 2021 at 9:56 Comment(2)
Cors-anywhere seems the better option as it is permanent and not temporary, justcors has to be renewed every day.Enzootic
Neither of those is useful in production.Mopup
K
6

Very easy solution (2 min to config) is to use local-ssl-proxy package from npm

The usage is straight pretty forward:
1. Install the package: npm install -g local-ssl-proxy
2. While running your local-server mask it with the local-ssl-proxy --source 9001 --target 9000

P.S: Replace --target 9000 with the -- "number of your port" and --source 9001 with --source "number of your port +1"

Koran answered 27/1, 2019 at 16:3 Comment(1)
github.com/cameronhunter/local-ssl-proxyConcoction
P
4

Solution for me was to just do it server side

I used the C# WebClient library to get the data (in my case it was image data) and send it back to the client. There's probably something very similar in your chosen server-side language.

//Server side, api controller

[Route("api/ItemImage/GetItemImageFromURL")]
public IActionResult GetItemImageFromURL([FromQuery] string url)
{
    ItemImage image = new ItemImage();

    using(WebClient client = new WebClient()){

        image.Bytes = client.DownloadData(url);

        return Ok(image);
    }
}

You can tweak it to whatever your own use case is. The main point is client.DownloadData() worked without any CORS errors. Typically CORS issues are only between websites, hence it being okay to make 'cross-site' requests from your server.

Then the React fetch call is as simple as:

//React component

fetch(`api/ItemImage/GetItemImageFromURL?url=${imageURL}`, {            
        method: 'GET',
    })
    .then(resp => resp.json() as Promise<ItemImage>)
    .then(imgResponse => {

       // Do more stuff....
    )}
Playreader answered 23/12, 2018 at 13:6 Comment(1)
I'm having the same issue with a streaming MP3. It will play but I can't get the byte array (if certain CORS conditions are present). Now I'm thinking I could do this in c# and send it myself, but its so ridiculous to have the bytes onboard and not be able to use them.Beattie
T
2

I had a similar problem with my browser debugger saying my response.body was null but fiddler and the developer tools show it as populated that turned out to be basically the same scenario as this. I was using a local Angular application hitting a Web Api service running on IISExpress. I fixed it by following the steps outlined here to find the correct applicationhost.config file to add a Access-Control-Allow-Origin header like so:

  <customHeaders>
    <clear />
    <add name="X-Powered-By" value="ASP.NET" />
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Headers" value="Content-Type" />
  </customHeaders>
Toad answered 20/6, 2022 at 2:40 Comment(0)
T
0

If all the above solutions don't work, probably it's because of the file permissions as sometimes even if you have fixed the non-cors problem using Heroku or another way, it throws 403 forbidden error. Set the directory/file permissions like this:

Permissions and ownership errors A 403 Forbidden error can also be caused by incorrect ownership or permissions on your web content files and folders.

Permissions Rule of thumb for correct permissions:

Folders: 755

Static Content: 644

Dynamic Content: 700

Temporary answered 25/6, 2021 at 15:48 Comment(0)
A
0

in my case removing of previously added headers: { 'Content-Type': 'application/json' } solved similar issue and works for me with no any mode cors/no-cors.

of course my backend has proper Access-Control-Allow-Origin: * header added to the response.

Austerlitz answered 13/3, 2023 at 23:21 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Undervest
S
0

I have to add some notes for anyone making CORS requests.

  • Make sure both websites use HTTPS. CORS only works over HTTPS if you want to set withCredentials: true and send over the cookie.
  • Configure CORS on the server side(eg, Nginx config).

Here is an example of Nginx config.

server {

    # CORS block BEGIN
    add_header 'Access-Control-Allow-Origin' $http_origin;
    add_header 'Access-Control-Allow-Headers' 'x-requested-with, Content-Type, origin, authorization, accept, client-security-token';
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,PATCH,DELETE';
    add_header 'Access-Control-Allow-Credentials' 'true';
    # CORS block END

}
  • $http_origin should only be used on the dev server. Replace it with your domain on the production server. For the rest, please continue your search.

References:

Spondee answered 26/3, 2023 at 10:36 Comment(0)
S
0

fetch(url, {mode: 'cors'})

works for me.

Basically, fetch(url, {requestParameterDataObject})

The fetch JS documentation will have more info about the specific field names within the request parameters object. Here, we are using the request's mode field & setting it to 'cors'.

My final code:

useEffect(() => {
        // Loading while etching API data
        setIsLoading(true);

        fetch(url, {mode: 'cors'})
            .then((response) => response.json())
            .then((data) => {
                setData(data);

                // Turn off loading flag once data comes back
                setIsLoading(false);
            });
    }, [url]);
Sisterly answered 1/5, 2023 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.