Execute web worker from different origin

It's not possible to load a web worker from a different domain.

Similar to your suggestion, you could make a fetch call, then take that JS and base64 it. Doing so allows you to do:

const worker = new Worker(`data:text/javascript;base64,${btoa(workerJs)}`)

You can find out more info about data URIs here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs.

This is the workaround I prefer because it doesn't require anything crazy like an iframe with a message proxy and is very simple to get working provided you setup CORS correctly from your CDN.


For those who find this question:

YES.

It is absolutely possible: the trick is leveraging an iframe on the remote domain and communicating with it through postMessage. The remote iframe (hosted on cdn.mydomain.com) will be able to load the webworker (located at cdn.mydomain.com/worker.js) since they both have the same origin. The iframe can then act as a proxy between the postMessage calls. The script.js will however be responsible from filtering the messages so only valid worker messages are handled.

The downside is that communication speeds (and data transfer speeds) do take a performance hit.

In short:

  • script.js appends iframe with src="//cdn.mydomain.com/iframe.html"
  • iframe.html on cdn.mydomain.com/iframe.html, executes new Worker("worker.js") and acts as a proxy for message events from window and worker.postMessage (and the other way around).
  • script.js communicates with the worker using iframe.contentWindow.postMessage and the message event from window. (with the proper checks for the correct origin and worker identification for multiple workers)

The best is probably to generate a simple worker-script dynamically, which will internally call importScripts(), which is not limited by this cross-origin restriction.

To understand why you can't use a cross-domain script as a Worker init-script, see this answer. Basically, the Worker context will have its own origin set to the one of that script.

// The script there simply posts back an "Hello" message
// Obviously cross-origin here
const cross_origin_script_url = "https://greggman.github.io/doodles/test/ping-worker.js";

const worker_url = getWorkerURL( cross_origin_script_url );
const worker = new Worker( worker_url );
worker.onmessage = (evt) => console.log( evt.data );
URL.revokeObjectURL( worker_url );

// Returns a blob:// URL which points
// to a javascript file which will call
// importScripts with the given URL
function getWorkerURL( url ) {
  const content = `importScripts( "${ url }" );`;
  return URL.createObjectURL( new Blob( [ content ], { type: "text/javascript" } ) );
}