I have a public github repo with some climate data at https://github.com/garyo/sea-surface-temp-viz/releases. Each day a github action creates a new release with some assets.
I'd like to create a separate web app that uses those resources (png and json files) to do some further processing/visualization, but I'm getting trapped by CORS issues. I can download the files in the browser, but my web app front end refuses due to missing CORS headers.
I know I could upload the files somewhere else where I'm in control of the outgoing headers (make an S3 account with keys, create a bucket, upload in my action, bla bla bla), but they're already there, with perfectly fine github URLs. Seems like it shouldn't be so painful.
I even tried using the github API to find the release, get the asset's raw URL and fetch that, but it has the same CORS problem even though the doc says it should support CORS. I even tried a free CORS proxy but couldn't get it to work. Why would anyone care about the origin of a github release asset?
Here's some code I tried:
async function getGithubAssets() {
const latestReleaseUrl = 'https://api.github.com/repos/garyo/sea-surface-temp-viz/releases/latest'
const result = await fetch(latestReleaseUrl)
const releaseData = await result.json()
const assets = releaseData.assets // array of assets
let sstTextureUrl = assets.find((elt: any) => elt.name == 'sst-temp-equirect.png').url
let anomTextureUrl = assets.find((elt: any) => elt.name == 'sst-temp-anomaly-equirect.png').url
let sstCmapUrl = assets.find((elt: any) => elt.name == 'sst-temp-equirect-cmap.json').url
let anomCmapUrl = assets.find((elt: any) => elt.name == 'sst-temp-anomaly-equirect-cmap.json').url
const fetch_opts = {
headers: {
'Accept': 'application/octet-stream'
}
}
return {
sstTexture: await (await fetch(sstTextureUrl, fetch_opts)).blob(),
anomTexture: await (await fetch(anomTextureUrl, fetch_opts)).blob(),
sstCmap: await (await fetch(sstCmapUrl, fetch_opts)).json(),
anomCmap: await (await fetch(anomCmapUrl, fetch_opts)).json()
}
}
Here's a ts playground to test it. Open dev tools, run it, you'll see the fetch errors.
The reason why you are getting CORS error is, first, looking at the raw asset link headers:
curl -I https://github.com/garyo/sea-surface-temp-viz/releases/download/2025-01-08/sst-temp-equirect.png
HTTP/2 302
server: GitHub.com
date: Thu, 09 Jan 2025 08:20:32 GMT
content-type: text/html; charset=utf-8
vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Encoding, Accept, X-Requested-With
location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/754842033/36cb52c4-f6ef-482d-8932-19c415a20442?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250109%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250109T082032Z&X-Amz-Expires=300&X-Amz-Signature=0e59c632ed3ade4f524024093b0da4dbae482e17ee929df931e30436afea042f&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dsst-temp-equirect.png&response-content-type=application%2Foctet-stream
cache-control: no-cache
strict-transport-security: max-age=31536000; includeSubdomains; preload
x-frame-options: deny
x-content-type-options: nosniff
x-xss-protection: 0
referrer-policy: no-referrer-when-downgrade
It is responding with a redirect status code, which will fail the preflight request (OPTIONS), since it doesn't support redirect.
Second, even if we succeed in doing the redirect, when trying to fetch the actual link, it does not have the appropriate CORS headers (Access-Control-Allow-Origin).
curl -I 'https://objects.githubusercontent.com/github-production-release-asset-2e65be/754842033/36cb52c4-f6ef-482d-8932-19c415a20442?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250109%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250109T082032Z&X-Amz-Expires=300&X-Amz-Signature=0e59c632ed3ade4f524024093b0da4dbae482e17ee929df931e30436afea042f&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dsst-temp-equirect.png&response-content-type=application%2Foctet-stream'
HTTP/2 200
content-type: application/octet-stream
last-modified: Wed, 08 Jan 2025 13:22:23 GMT
etag: "0x8DD2FE77CD40722"
server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: 3307a9e6-e01e-006f-44d1-61eba6000000
x-ms-version: 2024-11-04
x-ms-creation-time: Wed, 08 Jan 2025 13:22:23 GMT
x-ms-blob-content-md5: Bl11+QKpG+0BIBUDz9KupQ==
x-ms-lease-status: unlocked
x-ms-lease-state: available
x-ms-blob-type: BlockBlob
content-disposition: attachment; filename=sst-temp-equirect.png
x-ms-server-encrypted: true
via: 1.1 varnish, 1.1 varnish
fastly-restarts: 1
accept-ranges: bytes
age: 1949
date: Thu, 09 Jan 2025 08:22:23 GMT
x-served-by: cache-iad-kjyo7100150-IAD, cache-qpg120088-QPG
x-cache: HIT, HIT
x-cache-hits: 0, 0
x-timer: S1736410943.602729,VS0,VE1
content-length: 354996
Here are your options
If you have a running server/backend, instead of your frontend fetching directly to GitHub, you can instead request to your backend. Then in the backend, you can have an endpoint for fetching the GitHub assets. For example when using Express:
app.get('/asset', (req, res) => {
const response = await fetch('https://github.com/garyo/sea-surface-temp-viz/releases/download/2025-01-08/sst-temp-equirect.png');
const contentType = response.headers.get('content-type');
const buffer = await response.buffer();
res.set('Content-Type', contentType);
res.send(buffer);
});
(Of course if you use other backend, you need to adapt the code, but the idea is still the same)
You mentioned CORS proxy, and you are on the right track, especially if you don't have a backend. You can either use a self-hosted (cors-anywhere) or a hosted CORS proxy (Corsfix). Typically for a CORS proxy you will add the proxy URL before your target URL.
Example
fetch("https://proxy.corsfix.com/?https://github.com/garyo/sea-surface-temp-viz/releases/download/2025-01-08/sst-temp-equirect.png")
The CORS proxy will handle the redirect and add the appropriate CORS headers so you don't get the CORS errors when trying to make the request from your frontend.
You also mentioned using S3 to essentially mirror your GitHub release files, and that could work too. Since you will have control to the CORS headers in S3. I checked your code, and find that this is the route you went through. I'm still sharing my answer in case you are considering other available options.
(I am affiliated with Corsfix)