Files
next.js/docs
668094edce Turbopack: add experimental.turbopackWorkerAssetPrefix to keep Workers same-origin when assetPrefix is a CDN (#93271)
### What?

Adds `experimental.turbopackWorkerAssetPrefix` — a Turbopack counterpart
to webpack's
[`output.workerPublicPath`](https://webpack.js.org/configuration/output/#outputworkerpublicpath).
Opt-in, off by default, fully backward compatible.

```js
// next.config.js
module.exports = {
  assetPrefix: 'https://cdn.example.com',
  experimental: {
    // Same shape as `assetPrefix`: bare prefix, no trailing slash, no `/_next`.
    // `/_next/` is appended automatically. `''` → same-origin `/_next/...`.
    turbopackWorkerAssetPrefix: '',
  },
}
```

### Why?

When `assetPrefix` points to a cross-origin CDN (common production
setup), Turbopack emits Worker URLs under that CDN origin. Browsers
reject cross-origin `new Worker(url)` with `SecurityError`, breaking
every feature that uses `new Worker(new URL('./worker.ts',
import.meta.url))` — image decoders, WebP/GIF renderers, compression,
OffscreenCanvas, etc.

webpack solved this with `output.workerPublicPath`. Turbopack ignores
the `webpack()` callback in `next.config.js`, so the existing
same-origin workaround silently breaks when projects switch to
Turbopack.

Full design doc, alternatives considered, related issues (#88602,
#92676, #74621, #29468): **#93044**

Repro repo (`pnpm dev` reproduces, `pnpm dev:webpack` works):
https://github.com/GuinsooRocky/next-turbopack-worker-cdn-repro

### How?

Threads a new optional `turbopack_worker_asset_prefix: Option<RcStr>`
from `ExperimentalConfig` → `BrowserChunkingContext` → the JS runtime,
where `createWorker` reads it via a new `WORKER_BASE_PATH` global
emitted alongside the existing `CHUNK_BASE_PATH`. When unset, behavior
is identical to today.

Semantics, modeled on `assetPrefix`:

- **Bare prefix.** `/_next/` is appended automatically (same as
`computed_asset_prefix`); the user supplies just the origin (or empty
string for same-origin).
- **`undefined` vs `''`.** `undefined` falls back to the regular chunk
base path; `''` is a literal empty prefix that resolves to same-origin
`/_next/...`. The runtime distinguishes the two by injecting a JS `null`
literal vs a quoted string, and uses `WORKER_BASE_PATH ??
CHUNK_BASE_PATH` (not `||`).
- **Applies to entrypoint and module chunks.** The Turbopack worker
bootstrap rejects cross-origin module chunks (`Refusing to load script
from foreign origin`), so the override has to cover both — overriding
only the entrypoint would leave the worker unable to load any chunks
under a cross-origin `assetPrefix`.

Touchpoints:

- `packages/next/src/server/config-{shared,schema}.ts` — public option +
zod under `experimental`
- `crates/next-{api,core}/...` — wiring through to
`ClientChunkingContextOptions`;
`NextConfig::turbopack_worker_asset_prefix()` resolves to
`Some("<prefix>/_next/")` or `None`
- `turbopack/crates/turbopack-browser/src/chunking_context.rs` — new
field + builder + getter
- `turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs`
— emit `WORKER_BASE_PATH` declaration as `null` or a quoted string
- `turbopack/.../runtime-base.ts` — `createWorker` resolves
`workerBasePath` and uses it for both the entrypoint URL and
module-chunk URLs delivered via params
- `test/e2e/turbopack-worker-asset-prefix/` — e2e fixture using real
cross-origin (`localhost` page, `127.0.0.1` `assetPrefix`); intercepts
`new Worker()` to assert on the URL the runtime helper resolved. Three
cases: no override (cross-origin URL → `SecurityError`), explicit
override origin, and `''` (relative `/_next/...`).
- `docs/01-app/03-api-reference/08-turbopack.mdx` — option reference
- `telemetry/events/version.ts` — `useTurbopackWorkerAssetPrefix:
boolean` (set on any explicit value, including empty string)

### Notes

- Opening as **Draft** to invite API-shape feedback before going to
review. Per the contributing guide, the design discussion is at #93044.
- All checklist items the PR template requires for a feature are
covered: e2e tests, docs, telemetry, no error-link path applies.

cc @sokra

Closes #93044

<!-- NEXT_JS_LLM_PR -->

---------

Co-authored-by: Tobias Koppers <[email protected]>
2026-04-29 11:05:11 +02:00
..