Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question A simple FIX to handle the CORS issue in WebGL?

Discussion in 'Web' started by kuri_pl, Oct 30, 2023.

  1. kuri_pl

    kuri_pl

    Joined:
    Jun 21, 2016
    Posts:
    4
    Hello everyone!

    I've been struggling for so long with the Cross-Origin Resource Sharing (CORS) issue when making requests to APIs using UnityWebRequest in WebGL applications running in the browser.
    I've researched and tried various solutions, but they all seem to introduce layers of complexity I'd rather avoid.

    Has anyone tackled and resolved this issue in a way that could be considered "elegant", "simple", or even "ingenious"? Any advice or experiences you could share would be greatly appreciated.

    Thanks in advance!
     
  2. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    410
    This is a spectacularly annoying question to google, so hopefully the following helps you and others. If any experts feel I summarized this wrong, please correct.

    tl;dr: I don't believe there is a way, at least in code.

    To be clear, CORS is a mechanism by which the Browser Runtime (i.e. probably Chromium itself these days) blocks ALL data transfers from any domain to your code if they are on different domains, UNLESS the data is explicitly tagged *by the server serving it* as "Access-Control-Allow-Origin" that allows your domain (e.g. "Access-Control-Allow-Origin = "*" will be fine, but huge amounts of public/unsecured open data storage does not include that header.)

    CORS is "Browser-only Security", NOT general security. It's very possible that you can access a file from iOS, Android, Desktop, or your own server, and it will be fine, but when you try to access the same file within the browser, your sandboxed (js/wasm) code won't be allowed to. This is why WebGL developers get tripped up so often by it. You're probably thinking "but this file is clearly public, I can download it from wget, my code on Android, and even by pasting the URL into the browser". CORS says that even if those are true, YOUR CODE RUNNING IN A BROWSER SANDBOX still can't get the file unless the server hosting the file has explicitly said "it's OK for browser-sandboxed code to get this file. (To clarify, if you paste the DATA URL into the browser, that will always work, same as without CORS, since YOUR CODE is not asking for that data in this case, just Chromium is directly.)

    If you dig deep enough down through the layers of nonsensical webby wibble that's happening, Chrome (or any modern browser) will deny any code running within the browser access to ANY data sent from a different domain without a CORS header saying that it's OK. This is because security is the primary concern of the browser, attack vectors exist otherwise, and the solution to all web problems is to add more layers of nonsensical complexity until you can call it secure.

    Another common confusion is to paste the URL you're trying to access into your browser's address bar, observe the file opens correctly, and go "so why can't my code running in the browser download it?" The answer is that in the case of pasting into the address bar, there is no browser-sandboxed CODE running at all, so it's different from your code accessing the file. Chrome Address bar can always access a file without cors. Your code running in the browser can't.

    The ONLY real fix to this is by configuring the server. To be very clear, there is no way to modify your code to "avoid CORS". If you own the site that is hosting the files, you can do so by configuring the site appropriately, but you will have to google carefully to avoid loads of useless internet pageranked listicles. On the off chance you're using either Firebase or Google Cloud storage buckets, the answer is buried here:

    https://firebase.google.com/docs/storage/web/download-files

    And for an AWS S3 bucket it's here:

    https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html

    If you DO NOT own the DATA server (say you're trying to access a public file on the internet, say a public file on a company's google cloud storage bucket, or you're doing work for a client and their IT team isn't cooperative) that doesn't explicitly set access from your code's origin as allowed, then there is NO WAY, within your code, to fix this for your users. Web Developers think that this is fine, because they see this as an opportunity to build more needless complexity by "Building a REST API" to access the file server-side, which then re-transfers it onto your app, either appending a CORS header in the process, or running the API from within your own domain so that CORS isn't an issue.

    There is, at the time of writing, a ready-made API here: https://corsproxy.io/ . You can test your app in WebGL using corsproxy.io by escaping the url you want, and appending 'https://corsproxy.io/' to the start of the request string, and using your current UnityWebRequet code, but you are then subject to the reliability of that site, and you are of course channeling any data fully through it, so don't use it for authenticated requests. Also, I expect they won't take well to high-volume or large file requests. Theoretically, that site can arbitrarily modify the data before returning it to you so there's that. Security, eh? This is in no way a statement that I trust or you should trust data from corsproxy.io.

    For any real release, you'll want to run your own version of such a proxy. If you then think "surely I can run my own proxy server to forward stuff like this", on AWS Lambda or Google Cloud Functions, and it's 10 lines of code to recreate corsproxy.io (fetch file, append Allow-Origin header, retransmit file to client) but web developers abhor 5 lines of code when a 500 line solution using kubernetes, docker, chef, sonarqube, jenkins, terraform and ansible will do.

    Examples probably include:

    https://github.com/Rob--W/cors-anywhere
    https://github.com/taichunmin/gcf-cors-anywhere/blob/master/index.js

    I haven't checked to see if, for example, you want a large file, does this result in the server streaming the body across to you, or does it download the whole body then transfer the whole body. Also, be aware that you're going to be passing your full bandwidth through your server, so if it's a lot, you're paying. Also, if there is, say, a CDN'd setup for the file, then your users won't see that worldwide presence, just where you hosted your API.

    To be clear, there is no way, e.g. from direct Javascript, to circumvent CORS in a WebGL build, or on any web page.

    Hopefully this reply, at least the corsproxy.io bit for testing helps you out.

    You can also disable CORS in the the browser by either running chrome with a flag that, err, actually, disables ALL security, or by installing some chrome plugins at the top of google hits that I wouldn't touch with a bargepole.

    If you take all of this nonsense about CORS and security and multiply by the possible security risks from giving every user in the world's browser 64 bit processes and local GPU compute, you get the value of my confidence that WebGPU plus 64-bit address spaces will ever turn web browsers into a useful AAA gaming high performance computing environment. Plus, if that happened, the average user would have a browser that's more powerful than a native app running on a 4-year old smartphone, developers would be able to do nontrivial stuff within it instead of writing minimal react frontends to display server-side results and playing streaming video, and then how would the tech companies make money selling servers to do all that backend compute on?
     
    Last edited: Nov 3, 2023
    zhare86 likes this.
  3. jukka_j

    jukka_j

    Unity Technologies

    Joined:
    May 4, 2018
    Posts:
    953
    Thanks @ferretnt for a detailed and elaborate description of the issue. To echo a point, the CORS problem needs to be fixed on the configuration scripts of the destination web server where the web request is made to.

    It is not a Unity engine or C# code problem, and cannot be fixed, worked around or disabled there.

    If you do not want to have the hassle of managing resource sharing across origins, then by deploying all the resources that the game needs to run on the same origin (== same web server), the need to configure CORS at all will be eliminated.