Mitigate cross-origin isolation requirements for SharedArrayBuffer with Cloudflare workers
Joe Vallender • May 4, 2021
If you're responsible for a product serving a third party JavaScript widget onto client sites, you may be running into some warnings in devtools, or your clients might have been reaching out to you with regard to new requirements for their sites.
From Chrome 91, sites using SharedArrayBuffer must enable cross-origin isolation. Cross origin JavaScript that isn't correctly setup won't work.
The best full write up is here. But I'm going to run through a quick solution to the use case I encountered this week.
To simplify a bit: I have a product that serves a JavaScript file from Linode S3 (Same as AWS S3) to client sites, renders a widget with an iframe, and loads a web app from the product domain into that.
From reading the write up on web.dev you can see the solution is to set Cross-Origin-Resource-Policy cross-origin
headers on all requests and Cross-Origin-Embedder-Policy require-corp
on the iframe.
The release process for the product copies the latest build of files to S3, so I changed this to send the new headers along with the files. I went back, tested, refreshed and cleared caches but it didn't seem to have any effect. It looks like S3 currently only supports a common subset of headers.
Luckily all parts of the project are behind Cloudflare, so I can change the headers using a worker.
Log into your Cloudflare account and go into the relevant site. From the top menu click Workers > Manager workers > Create worker
to start editing a worker.
Here's the code
async function handleRequest(request) {
request = new Request(request)
request.headers.set("Cross-Origin-Resource-Policy", "cross-origin")
let response = await fetch(request)
response = new Response(response.body, response)
if(request.url.indexOf('https://domain.com/external') === 0) {
response.headers.set("Cross-Origin-Embedder-Policy", 'require-corp')
}
response.headers.set("Cross-Origin-Resource-Policy", 'cross-origin')
return response
}
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
This will set Cross-Origin-Resource-Policy cross-origin
on everything (don't worry, we'll pin it down to specific URLs next) and will add Cross-Origin-Embedder-Policy require-corp
to URLs starting with https://domain.com/external
. In my case all off-site webapps run from the product domain (to easily access a JWT from localStorage) on a common path prefix.
Save your work and click Workers on the top nav again. And click the Add route
button to tell Cloudflare when to run your worker. There is a 100K request limit on the free Cloudflare plan so make sure to pin the URLs down accurately. For example I ran the worker on iframes pointing at https://domain.com/external/*
and a few specific https://assets.domain.com/file.js
files.
The workers kick in almost instantly once associated with a URL, so check your widget and hopefully now it's all working!
If not, try clearing your cache. Some assets may be cached in the browser, and you might not be requesting Cloudflare yet.
If your product deals with a lot of requests, consider caching the response of your fetch, because both the request to your worker and any requests the worker itself makes count towards the limit
let response = await fetch(request, {
cf: {
cacheTtl: [number of seconds],
cacheEverything: true,
},
})