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