Skip to main content
Move heavy work off the main thread of a page without freezing it. Extension.js compiles new Worker(new URL("./worker.js", import.meta.url)) through the bundler default for a web target. The call emits a separate worker chunk at the root of dist/<browser>/, and the compiled call points at that chunk through chrome.runtime.getURL(). That part works. The browser is what refuses. A worker script has to come from the same origin as the document that creates it. The document behind a content script is the host page, so both forms below fail.

What a content script actually gets

This content script compiles cleanly and fails at runtime:
On https://example.com, Chromium reports:
Adding the file to web_accessible_resources does not lift the restriction. It controls whether the page may load the file, not whether the file may become a worker for a foreign origin.
This was measured on Chromium with a content script on a public page. Firefox and Safari were not measured, so treat the errors above as Chromium behavior.

Run the worker from a page that the extension owns

A popup, an options page, a side panel, and an offscreen document all load from the chrome-extension:// origin. A worker created there is same-origin, so both forms work with no extra setup:
This is the route to prefer. Keep the content script thin, send the input to your background service worker or to an offscreen document, and let the worker run there. See Offscreen documents for a page that runs without any visible surface, and Message passing for the round trip.

The blob workaround, and its two conditions

When the work has to stay in the page, fetch the worker source and construct the worker from a blob URL. A blob inherits the page origin, so the browser accepts it:
Two conditions decide whether that succeeds. The file has to be web accessible. extension build does not add the worker chunk to web_accessible_resources, so declare the file yourself in manifest.json:
Without that entry the fetch fails with TypeError: Failed to fetch, and the request shows as chrome-extension://invalid/. The host page CSP has to allow blob workers. A page that sends worker-src 'self' blocks the blob, and the failure is quiet. The constructor returns normally, then the worker fails through onerror with an empty message:
A try/catch around new Worker never sees it. Attach an onerror handler and treat it as the real failure signal.

Choosing between the three

  • An extension page needs no setup, and both forms work there.
  • A content script with a blob needs a manifest entry and a permissive page CSP.
  • A content script with an extension URL is refused, and nothing changes that.

Next steps