Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 90 additions & 10 deletions files/en-us/web/api/workerglobalscope/importscripts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,29 @@ browser-compat: api.WorkerGlobalScope.importScripts

{{APIRef("Web Workers API")}}{{AvailableInWorkers("worker")}}

> [!WARNING]
Copy link
Collaborator Author

@hamishwillee hamishwillee Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbamberg This follows same pattern as the others. The method is used to import (one or more) classic scripts into a classic module.

I have done quite a lot of testing.

  1. FF will silently fail to load a worker or import a script with UTF-16 encoding. The web prefers UTF8 but the fact this fails silently is irritating, because I assumed my files would default to UTF8.
    • Do you (also) think a BCD note on the worker itself makes sense?
  2. Both the initial script and subsequent importScript operation appear to be fetched in no-cors mode.
  3. BUT from my testing the initial script MUST be same-origin with the worker or a blob or data type.
  4. I expected same-origin importScript to work, and it does.
  5. Not entirely sure what I exected for importScript cross origin, but it seems to work fine - or at least a console.log() in the loaded script runs. That's with no special headers or settings on either server.
  6. You can block the load by having a CORP on the cross-origin file to be loaded.
  7. However if you add a CSP for the worker-src it seem to be ignored for the nested importScript (though it obeys it for the top level import in that you can set none. That's my testing anyway.

Essentially it looks like for this method you can import what you like from wherever you like unless blocked by the server resource policy you are importing from.

FYI, my testing is here https://github.com/hamishwillee/various_webapi_tests/tree/main/classic_worker_import - this uses a caddyfile - caddy allows you to set up a server with headers, https and certificate with no real effort..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS I have not tried to capture all that in this doc. Waiting to see your thoughts.

> The parameters passed to this method represent the URLs of classic scripts to be imported into a worker.
> APIs like this are known as [injection sinks](/en-US/docs/Web/API/Trusted_Types_API#concepts_and_usage), and are potentially a vector for [cross-site scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS) attacks.
>
> You can mitigate this risk by having a [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP) that restricts the locations from which scripts can be loaded, and by always assigning {{domxref("TrustedScriptURL")}} objects instead of strings and [enforcing trusted types](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types).
> See [Security considerations](#security_considerations) for more information.

The **`importScripts()`** method of the {{domxref("WorkerGlobalScope")}} interface synchronously imports one or more scripts into the worker's scope.

## Syntax

```js-nolint
importScripts(path0)
importScripts(path0, path1)
importScripts(path0, path1, /* …, */ pathN)
importScripts(url0)
importScripts(url0, url1)
importScripts(url0, url1, /* …, */ urlN)
```

### Parameters

- `pathN`
- : A string value representing the URL of the script to be imported. The URL may be absolute or relative. If the URL is relative, it is relative to the worker entry script's URL.
- `urlN`
- : A {{domxref("TrustedScriptURL")}} instance or a string representing the URL of the script to be imported.
The URL may be absolute or relative.
If the URL is relative, it is relative to the worker entry script's URL.

### Return value

Expand All @@ -30,22 +39,93 @@ None ({{jsxref("undefined")}}).
### Exceptions

- `NetworkError`
- : Imported scripts were served without a `text/javascript` MIME type or without one of the permitted [legacy JavaScript MIME types](/en-US/docs/Web/HTTP/Guides/MIME_types#legacy_javascript_mime_types).
- : Imported scripts were served without a `text/javascript` media (MIME) type or without one of the permitted [legacy JavaScript MIME types](/en-US/docs/Web/HTTP/Guides/MIME_types#legacy_javascript_mime_types).
- {{jsxref("TypeError")}}
- : Thrown if the current {{domxref("WorkerGlobalScope")}} is a module. Use [`import`](/en-US/docs/Web/JavaScript/Reference/Statements/import) instead.
- : Thrown if the current {{domxref("WorkerGlobalScope")}} is a module (use [`import`](/en-US/docs/Web/JavaScript/Reference/Statements/import) instead).
It may also happen if any parameter is not a {{domxref("TrustedScriptURL")}} and the site is [enforcing trusted types](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types).

## Description

The **`importScripts()`** method synchronously imports one or more scripts into the worker's scope.

Unlike the initial classic module script, which must be same-origin with its document, this method can import scripts that are cross-origin unless blocked by a resource {{httpheader("Cross-Origin-Resource-Policy")}} or some other security mechanism.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where I'd add a bit more on the importing rules for classic scripts https://github.com/mdn/content/pull/42396/files#r2621680224


### Security considerations

The parameters specify scripts to be imported into the scope of a classic worker.
If the input is provided by a user, this is a possible vector for [cross-site scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS) attacks.

It is extremely risky to accept and execute arbitrary URLs from untrusted origins.
A website should control what scripts that are allowed to run using a [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP) with the [`worker-src`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/worker-src) directive (or a fallback defined in [`script-src`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src) or [`default-src`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/default-src)).
This can restrict scripts to those from the current origin, or a specific set of origins, or even particular files.

If you're using this property and [enforcing trusted types](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) (using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive), you will need to always assign {{domxref("TrustedScriptURL")}} objects instead of strings.
This ensures that the input is passed through a transformation function, which has the chance to reject or modify the URL before it is injected.

## Examples

If you had some functionality written in a separate script called `foo.js` that you wanted to use inside `worker.js`, you could import it using the following line:
### Basic usage

If you had some functionality written in a separate script called `foo.js` in the same directory as `worker.js`, you could import it into the worker using the following line:

```js
importScripts("foo.js");
```

`foo.js` should be in the same URL subtree as the worker's entry point — for example, if this worker was created with `new Worker("worker.js")`, then `worker.js` is the entry point. If `worker.js` is at `https://example.com/scripts/worker.js`, then `foo.js` should be at `https://example.com/scripts/foo.js`.

`importScripts()` and `self.importScripts()` are effectively equivalent — both represent `importScripts()` being called from inside the worker's inner scope.

Note that in the next section we show you how to pass a `TrustedScriptURL` instead of a string.
This was omitted in this example for brevity, but is recommended in production code.

### Using TrustedScriptURL

To mitigate the risk of XSS, we should always assign `TrustedScriptURL` instances to each of the parameters.
We also need to do this if we're enforcing trusted types for other reasons and we want to allow some script sources that have been permitted (by `CSP: worker-src`).

Trusted types are not yet supported on all browsers, so first we define the [trusted types tinyfill](/en-US/docs/Web/API/Trusted_Types_API#trusted_types_tinyfill).
This acts as a transparent replacement for the trusted types JavaScript API:

```js
if (typeof trustedTypes === "undefined")
trustedTypes = { createPolicy: (n, rules) => rules };
```

Next we create a {{domxref("TrustedTypePolicy")}} that defines a {{domxref("TrustedTypePolicy/createScriptURL", "createScriptURL()")}} method for transforming input strings into {{domxref("TrustedScriptURL")}} instances.

For the purpose of this example we'll assume that we want to allow a predefined set of URLs in the `scriptAllowList` array and log any other scripts.

```js
const scriptAllowList = [
// Some list of allowed URLs
];
const policy = trustedTypes.createPolicy("script-url-policy", {
createScriptURL(input) {
if (scriptAllowList.includes(input)) {
return input; // allow the script
}
console.log(`Script not in scriptAllowList: ${input}`);
return ""; // Block the script
},
});
```

Then we use the `policy` object to create a `trustedScript` object from a potentially unsafe input string:

```js
// The potentially malicious string
// We won't be including untrustedScript in our scriptAllowList array
const untrustedScript = "https://evil.example.com/import_worker.js";

// Create a TrustedScriptURL instance using the policy
const trustedScriptURL = policy.createScriptURL(untrustedScript);
```

The `trustedScriptURL` property can now be used when importing the script in a classic worker:

```js
importScripts(trustedScriptURL);
```

## Specifications

{{Specifications}}
Expand Down