# script-src (/en/docs/web-security/policies/content-security-policy/directives/script-src)



The `script-src` directive in a Content Security Policy (CSP) decides which scripts a page is allowed to load and run. It governs `<script>` elements, inline scripts and event handlers, `javascript:` URLs, `eval()` and similar dynamic evaluation, and the scripts that workers run. If a script does not match `script-src`, the browser refuses to execute it. This is the most important directive for stopping cross-site scripting (XSS).

`script-src` is the umbrella for two finer directives, [`script-src-elem`](/en/docs/web-security/policies/content-security-policy/directives/script-src-elem) for `<script>` elements and [`script-src-attr`](/en/docs/web-security/policies/content-security-policy/directives/script-src-attr) for inline event handler attributes. When you set those, they take over their part of the job and `script-src` becomes their fallback.

A minimal safe policy for this directive:

```http
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'
```

## Fallback chain [#fallback-chain]

`script-src` falls back to [`default-src`](/en/docs/web-security/policies/content-security-policy/directives/default-src). If you set `default-src` and omit `script-src`, scripts are checked against `default-src`. If you set `script-src`, it fully replaces `default-src` for scripts.

The finer directives fall back through `script-src`:

* `script-src-elem` falls back to `script-src`, then `default-src`.
* `script-src-attr` falls back to `script-src`, then `default-src`.

So a single `script-src` covers both `<script>` elements and inline handlers unless you override one of them.

## Values [#values]

`script-src` takes a space-separated source list, or `'none'` to block all scripts.

| Value                                                     | Status          | Description                                                                                                                                |
| --------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `'none'`                                                  | ✅ Good          | Blocks all script. Used alone.                                                                                                             |
| `'self'`                                                  | ✅ Good          | Scripts from your own origin only.                                                                                                         |
| Host source                                               | ✅ Good          | A specific host such as `https://cdn.example.com`.                                                                                         |
| `https:`                                                  | ❌ Risky         | Any HTTPS origin may serve script, close to having no allowlist.                                                                           |
| `data:`                                                   | ❌ Risky         | Attacker-controlled `data:` URLs execute as script.                                                                                        |
| `blob:`                                                   | ❌ Risky         | `blob:` URLs execute as script, an XSS vector.                                                                                             |
| `'nonce-...'`                                             | ✅ Good          | Per-response random token, unique and unguessable.                                                                                         |
| `'sha256-...'`                                            | ✅ Good          | Digest of an exact inline block or external file.                                                                                          |
| `'strict-dynamic'`                                        | ✅ Good          | Trust flows from nonce or hash allowed scripts; host and scheme sources are ignored.                                                       |
| `'wasm-unsafe-eval'`                                      | ✅ Good          | WebAssembly compilation only, narrower than `'unsafe-eval'`.                                                                               |
| `'report-sample'`                                         | ✅ Good          | Adds the first 40 characters of blocked inline code to reports.                                                                            |
| `'trusted-types-eval'`                                    | ✅ Good          | Allows eval only with TrustedScript when Trusted Types are enforced. Recently became available across current Chrome, Firefox, and Safari. |
| `'unsafe-inline'`                                         | ❌ Risky         | Allows every inline script, including injected ones.                                                                                       |
| `'unsafe-eval'`                                           | ❌ Risky         | Allows `eval()`, `new Function()`, and string timers.                                                                                      |
| `'unsafe-hashes'`                                         | ❌ Risky         | Lets hashes match inline event handlers, re-enabling that surface.                                                                         |
| `'inline-speculation-rules'`                              | 🧪 Experimental | Inline speculation rules scripts. Chromium-led, not on the standards track.                                                                |
| `'report-sha256'` / `'report-sha384'` / `'report-sha512'` | 🧪 Experimental | [Report-only hash collection](/en/docs/web-security/policies/content-security-policy/values/report-sha-keyword). Chromium-only.            |

It accepts:

* [Keyword sources](/en/docs/web-security/policies/content-security-policy/values/csp-keywords): `'self'`, `'none'`, `'unsafe-inline'`, `'unsafe-eval'`, `'wasm-unsafe-eval'`, `'strict-dynamic'`, `'unsafe-hashes'`, `'report-sample'`, `'trusted-types-eval'`, and `'inline-speculation-rules'`.
* A [nonce or hash](/en/docs/web-security/policies/content-security-policy/values/csp-hashes-nonce) (`'nonce-...'`, `'sha256-...'`) to allow specific inline or external scripts.
* A [host source](/en/docs/web-security/policies/content-security-policy/values/csp-host-source) such as `https://cdn.example.com`.
* A [scheme source](/en/docs/web-security/policies/content-security-policy/values/csp-scheme-source) such as `https:`.

Two interactions are worth knowing up front. Adding a nonce or hash makes `'unsafe-inline'` ignored, so older browsers that do not understand nonces still get the inline restriction. Adding `'strict-dynamic'` makes host and scheme allowlist entries ignored, trust flows from a nonce or hash instead.

## Examples [#examples]

A nonce-based policy that allows same-origin scripts and one inline script tagged with a matching nonce:

```http
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m'
```

## Common use [#common-use]

Most pages start by allowing their own origin and a small set of trusted hosts:

```http
Content-Security-Policy:
    script-src 'self' https://cdn.example.com;
    object-src 'none';
    base-uri 'none'
```

The modern recommendation is a strict CSP built on a nonce or hash plus `'strict-dynamic'`, rather than a list of allowed hosts. A host allowlist is easy to bypass through an open redirect or a JSONP endpoint on an allowed domain, while a nonce-based policy only trusts the exact scripts you mark. See the how-to on [strict-dynamic](/en/blog/strict-dynamic-csp) and the [nonce setup guide](/en/blog/csp-nonce-setup).

## Security notes [#security-notes]

`script-src` is the core XSS control. An injected `<script>` or inline handler runs only if it matches the directive, so a tight `script-src` turns an injection into a blocked, reported violation instead of code execution.

* `'unsafe-inline'` defeats most of the protection: it allows any inline script, including injected ones. Remove it and switch to a nonce or hash. See [why to drop unsafe-inline](/en/blog/unsafe-inline-csp).
* `'unsafe-eval'` allows `eval()`, `new Function()`, and string `setTimeout`. Avoid it where you can; many libraries no longer need it.
* `'wasm-unsafe-eval'` is the narrow alternative that allows WebAssembly compilation only, without enabling general `eval()`.

You can validate a policy quickly with the [CSP evaluator](/tools/csp-evaluator), which flags weak `script-src` values before you ship them.

## Known bypasses and risks [#known-bypasses-and-risks]

A host allowlist is the common weak point. If an allowed origin hosts a JSONP callback, an open redirect, or a copy of a permissive framework, an attacker can load script through it. `'strict-dynamic'` sidesteps this by ignoring the allowlist and trusting only nonce or hash allowed scripts and what they create.

`'unsafe-hashes'` widens hash matching to inline event handlers and is sometimes needed for legacy markup, but it loosens the policy, prefer moving handlers into nonce-tagged script files. A wildcard such as `*` or a broad scheme like `https:` allows script from almost anywhere and should be treated as close to having no `script-src` at all.

## Recommendation [#recommendation]

For `script-src`, ship a strict nonce-based policy instead of a host allowlist,
inside a complete base policy that also sets the other important directives:

```http
Content-Security-Policy:
    default-src 'self';
    script-src 'nonce-{RANDOM}' 'strict-dynamic';
    style-src 'self';
    img-src 'self';
    font-src 'self';
    connect-src 'self';
    media-src 'self';
    manifest-src 'self';
    frame-src 'none';
    worker-src 'self';
    object-src 'none';
    base-uri 'none';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
    report-to csp-endpoint
```

Pair it with the endpoint declaration in a separate block:

```http
Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
```

This is the strict CSP that the [OWASP CSP cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html) and [web.dev](https://web.dev/articles/strict-csp) recommend, extended with every no-fallback directive so nothing is left implicit. `'strict-dynamic'` makes host and scheme sources irrelevant, so trust comes only from the per-response nonce and propagates to the scripts your allowed code loads. Regenerate the nonce on every response and keep `'unsafe-inline'` and `'unsafe-eval'` out.

## Reporting [#reporting]

When a script is blocked, the browser sends a [csp-violation report](/en/docs/web-security/reporting-api/reports/csp-violation) naming `script-src` (or the resolved `script-src-elem` / `script-src-attr`) as the effective directive. Add `'report-sample'` to include a short sample of the blocked code in the report, which helps you identify the offending script. Point your policy at an endpoint to collect these:

```http
Content-Security-Policy:
    script-src 'self' 'nonce-r4nd0m';
    report-to csp-endpoint
```

CentralCSP aggregates these reports and builds a [script inventory](/platform/supply-chain) of everything that runs on your pages, so you can see real `script-src` usage before you tighten the directive.

## Browser support [#browser-support]

`script-src` and its core keywords, including `'strict-dynamic'`, `'unsafe-eval'`, and `'unsafe-inline'`, are widely supported. `'wasm-unsafe-eval'` is widely supported across current browsers. `'trusted-types-eval'` recently became available across current Chrome, Firefox, and Safari. `'inline-speculation-rules'` is Chromium-led, available in Safari only behind a flag, and not on the standards track. The `'report-sha256'` family is Chromium-only.

## FAQ [#faq]

### Should I use a nonce or a hash in script-src? [#should-i-use-a-nonce-or-a-hash-in-script-src]

Use a nonce for server-rendered pages whose inline scripts change per response;
the server sets a fresh random token on every response and tags each trusted
script with it. Use a hash for static inline scripts that never change. Both
suppress `'unsafe-inline'`, so older browsers still get the inline restriction.

### What does strict-dynamic do? [#what-does-strict-dynamic-do]

`'strict-dynamic'` lets trust flow from a script the policy already allowed
through a nonce or hash to any further scripts that script creates, so a trusted
loader can pull in its dependencies. In exchange, host and scheme allowlist
entries are ignored, which removes the open-redirect and JSONP bypasses a host
allowlist carries.

### Does script-src stop all XSS? [#does-script-src-stop-all-xss]

No. `script-src` is the core control that turns an injected script into a
blocked, reported violation instead of code execution, but it is not complete on
its own. Pair it with `object-src 'none'` and `base-uri 'none'` to close plugin
and base-tag vectors, and add Trusted Types to lock down DOM sinks.

## See also [#see-also]

* [script-src-elem](/en/docs/web-security/policies/content-security-policy/directives/script-src-elem)
* [script-src-attr](/en/docs/web-security/policies/content-security-policy/directives/script-src-attr)
* [default-src](/en/docs/web-security/policies/content-security-policy/directives/default-src)
* [CSP keyword values](/en/docs/web-security/policies/content-security-policy/values/csp-keywords)
* [Nonces and hashes](/en/docs/web-security/policies/content-security-policy/values/csp-hashes-nonce)
* [CSP evaluator](/tools/csp-evaluator)

## Sources [#sources]

* [MDN, CSP script-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src)
* [W3C, Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
* [web.dev, Mitigate XSS with a strict CSP](https://web.dev/articles/strict-csp)
* [OWASP, Content Security Policy cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
