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

JavascriptReactjsCorsCreate React-AppFetch Api

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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 https://stackoverflow.com/questions/39109789/what-limitations-apply-to-opaque-responses/39109790#39109790 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 https://stackoverflow.com/questions/39109789/what-limitations-apply-to-opaque-responses/39109790#39109790, 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 https://stackoverflow.com/questions/39109789/what-limitations-apply-to-opaque-responses/39109790#39109790 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.

Solution 2 - Javascript

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.

Solution 3 - Javascript

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

Solution 4 - Javascript

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

  1. 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"

Solution 5 - Javascript

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>

Solution 6 - Javascript

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....
    )}

Solution 7 - Javascript

If you are trying to address this issue temporarily on your localhost, you can use this chrome extension : Allow CORS Access-Control-Allow-Origin https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf

Solution 8 - Javascript

> The simple solution: Add the following to the very top of the php file you are requesting the data from.

header("Access-Control-Allow-Origin: *");

> NOTE: This method is not secure and is not recommended for a production environment!

Solution 9 - Javascript

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

Solution 10 - Javascript

Code Postman

enter image description here

Javascript

function fnlogin(){

var url = ("http://localhost:3000/users/login");
var method =('POST');
var data = JSON.stringify({
    "email": "[email protected]",
    "password": "12345"
  });

  
  var xhr = new XMLHttpRequest();
  xhr.withCredentials = false; // SOLUTION False not True
  
  xhr.addEventListener("readystatechange", function () {
    if (this.readyState === 4) {
      console.log(this.responseText);
    }
  });
  
  xhr.open(method, url ,true);
  xhr.setRequestHeader("content-type", "application/json");
  xhr.setRequestHeader("cache-control", "no-cache");
  //xhr.send(data,{mode: 'no-cors'});
  xhr.send(data);

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondwwwView Question on Stackoverflow
Solution 1 - JavascriptsideshowbarkerView Answer on Stackoverflow
Solution 2 - JavascriptdotNETView Answer on Stackoverflow
Solution 3 - JavascriptVarnit RohillaView Answer on Stackoverflow
Solution 4 - JavascriptvolnaView Answer on Stackoverflow
Solution 5 - JavascriptHerman MartinusView Answer on Stackoverflow
Solution 6 - JavascriptStuart AitkenView Answer on Stackoverflow
Solution 7 - JavascriptVahidKKView Answer on Stackoverflow
Solution 8 - JavascriptReally Nice CodeView Answer on Stackoverflow
Solution 9 - JavascriptNAVNEET CHANDANView Answer on Stackoverflow
Solution 10 - Javascriptfernando yevenesView Answer on Stackoverflow