RoastMyOpsec

Safety

CSP worker-src vs script-src for Workers and Service Workers

April 14, 2023

CSP worker-src vs script-src is two different script surfaces. script-src allowlists JavaScript that runs in the page. worker-src allowlists URLs that may be used as Worker, SharedWorker, and Service Worker scripts. If you only tighten script-src and leave worker-src to a loose default-src or a fallback, a XSS gadget can still start a worker from an origin you never meant to trust. Most brochure sites that do not register workers should set worker-src 'none'. Sites that do use a first-party service worker should name that origin — usually 'self' — not *. RoastMyOpsec reads CSP quality on the landing HTML. It does not score missing worker-src as a standalone F, does not register a service worker, and does not exploit worker scripts.

The practical difference

A page script is script-src. A DedicatedWorker, SharedWorker, or Service Worker constructor/register call is a worker-src fetch. child-src is the older combined directive for workers and frames; modern policies split worker-src and frame-src. If worker-src is omitted, browsers fall back to child-src, then script-src, then default-src — which is why a 'tight' script-src still surprises teams that never named workers.

Service workers persist and intercept fetches. That is a product feature, not a reason to set worker-src *. Pair a first-party worker with Cache-Control on HTML and a clear unregister path on logout if the worker held authenticated caches. See service workers vs Cache-Control.

How to choose worker-src

Marketing site with no workers: worker-src 'none' on day one, same reflex as object-src 'none'. PWA or offline shell: worker-src 'self' and keep the worker file on your origin with SRI or a hashed filename. Third-party 'analytics worker' URLs: name that origin only if you truly need it; prefer first-party analytics. Report-Only first if you are not sure whether a tag manager starts a worker.

DirectiveWhat it allowlistsDoes not replaceTakeaway
script-srcClassic page JavaScriptWorker constructor URLsNecessary, not sufficient
worker-src 'none'No workersscript-src / object-srcDefault for brochure sites
worker-src 'self'First-party worker scriptsCache-Control on HTMLPWAs and first-party SW
child-srcLegacy workers + frames mixA split modern policyPrefer worker-src + frame-src

When worker-src wins

worker-src wins when the page CSP looks strict but nobody named workers. script-src nonce/hash still wins for inline XSS on the document. Neither is a substitute for not shipping a service worker that caches personalized HTML as public.

What the roast can prove

CSP presence and quality signals on the HTML response can surface. Missing worker-src is not scored as its own finding. Confirm the directive (or the fallback chain) in the header. Pair with object-src and connect-src so a worker cannot fetch the whole internet even if it starts.

Common mistakes

The first mistake is assuming script-src covers Worker() and navigator.serviceWorker.register.

The second mistake is worker-src https: because one CDN hosts a helper file.

The third mistake is a service worker on 'self' that caches logged-in HTML as if it were a public asset.

Free audit the URL you own

RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.

Free audit now

FAQ

Does script-src already control service workers?
Not reliably. Set worker-src explicitly. Fallback to script-src only happens when worker-src and child-src are both absent.
Should a brochure site set worker-src 'none'?
Yes if it does not register workers. Missing it is not a roast F by itself if default-src is already tight.
Does RoastMyOpsec score worker-src?
It grades CSP quality overall. It does not register a service worker or treat worker-src as the only CSP check.

Sources

Related guides