{
  "doc": {
    "id": "app/configure/content-security-policy",
    "title": "Content security policy (CSP) in Cypress",
    "description": "Why Cypress manages Content Security Policy (CSP), how to tell when you need to configure it, and how to allow your policy through for testing.",
    "section": "app",
    "source_path": "/llm/markdown/app/configure/content-security-policy.md",
    "version": "e4f4d57da4cfedd520d3d465137d387fd54b532f",
    "updated_at": "2026-09-08T17:49:12.214Z",
    "headings": [
      {
        "id": "app/configure/content-security-policy#content-security-policy",
        "text": "Content Security Policy",
        "level": 1
      },
      {
        "id": "app/configure/content-security-policy#why-cypress-needs-to-manage-csp",
        "text": "Why Cypress needs to manage CSP",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#signs-you-need-to-configure-csp-handling",
        "text": "Signs you need to configure CSP handling",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#what-configuring-csp-handling-gives-you",
        "text": "What configuring CSP handling gives you",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#how-csp-is-delivered",
        "text": "How CSP is delivered",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#allowing-the-header-through-with-experimentalcspallowlist",
        "text": "Allowing the header through with experimentalCspAllowList",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#configuration-examples",
        "text": "Configuration examples",
        "level": 3
      },
      {
        "id": "app/configure/content-security-policy#always-stripped-directives",
        "text": "Always-stripped directives",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#nonce-injection",
        "text": "Nonce injection",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#interaction-with-code-modifying-options",
        "text": "Interaction with code-modifying options",
        "level": 2
      },
      {
        "id": "app/configure/content-security-policy#see-also",
        "text": "See also",
        "level": 2
      }
    ]
  },
  "chunks": [
    {
      "id": "app/configure/content-security-policy#why-cypress-needs-to-manage-csp",
      "doc_id": "app/configure/content-security-policy",
      "heading": "Why Cypress needs to manage CSP",
      "heading_level": 2,
      "content_markdown": "## Why Cypress needs to manage CSP\n\nTo drive your application, Cypress injects its own `<script>` tags into the page and loads your app inside an iframe. A strict CSP is designed to stop precisely this kind of injection: an unmodified policy can block Cypress' scripts from executing, forbid the app from being framed, or restrict the DOM rewriting Cypress relies on. In other words, if Cypress left your CSP untouched, adding a strict policy to your application would stop Cypress from being able to test it.\n\nTo avoid this, **Cypress removes CSP by default.** When a policy is delivered over HTTP headers, Cypress strips both the `Content-Security-Policy` and `Content-Security-Policy-Report-Only` headers from the response before it reaches the browser, so the browser never enforces the policy during a test run. For most tests, where you are exercising application behavior rather than the policy itself, this is invisible and requires no configuration.\n",
      "section": "app",
      "anchors": [
        "why-cypress-needs-to-manage-csp"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 201
    },
    {
      "id": "app/configure/content-security-policy#signs-you-need-to-configure-csp-handling",
      "doc_id": "app/configure/content-security-policy",
      "heading": "Signs you need to configure CSP handling",
      "heading_level": 2,
      "content_markdown": "## Signs you need to configure CSP handling\n\nBecause Cypress strips CSP by default, two different symptoms point in opposite directions:\n\n*   **Your app or its scripts are blocked even though Cypress strips headers.** This usually means the policy is delivered with a [`<meta>` tag](#how-csp-is-delivered) rather than an HTTP header, or that a directive Cypress does not strip is interfering. Look for `Refused to load…` / `Refused to execute…` CSP violation messages in the browser console.\n*   **You actually want the policy enforced.** If your goal is to _verify your own CSP_ (for example asserting that a violation is reported, or that a forbidden resource is blocked), the default behavior works against you, because the policy you are trying to test has been removed. You will see your CSP simply not taking effect during the test.\n\nIf either of these describes you, you need to opt in to Cypress sending the CSP header through to the browser.\n",
      "section": "app",
      "anchors": [
        "signs-you-need-to-configure-csp-handling"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 209
    },
    {
      "id": "app/configure/content-security-policy#what-configuring-csp-handling-gives-you",
      "doc_id": "app/configure/content-security-policy",
      "heading": "What configuring CSP handling gives you",
      "heading_level": 2,
      "content_markdown": "## What configuring CSP handling gives you\n\nOpting in lets you **run your tests against a real, enforced policy** instead of no policy at all. This is valuable when:\n\n*   You are testing the CSP implementation itself (reporting, blocking, allowed sources).\n*   Your application's behavior legitimately depends on CSP being present.\n*   You want your test environment to mirror production as closely as possible.\n\nThe trade-off is that Cypress still has to keep itself working, so the enforced policy is a _modified_ version of yours rather than a byte-for-byte copy. See [Always-stripped directives](#always-stripped-directives) and [Nonce injection](#nonce-injection) for exactly what changes.\n",
      "section": "app",
      "anchors": [
        "what-configuring-csp-handling-gives-you"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 133
    },
    {
      "id": "app/configure/content-security-policy#how-csp-is-delivered",
      "doc_id": "app/configure/content-security-policy",
      "heading": "How CSP is delivered",
      "heading_level": 2,
      "content_markdown": "## How CSP is delivered\n\nThere are two ways to deliver a CSP, and Cypress treats them very differently:\n\n*   [HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy): configurable, and the only form Cypress can allow through for testing.\n*   [Meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#http-equiv): supported automatically, but **not** configurable.\n\nCypress functions with the `<meta>` tag implementation without any configuration required. This works because Cypress loads its `<script>` tags into your application before any `<meta>` tag is parsed, so the CSP directives are never applied to the script loaded by Cypress. This relies on that ordering rather than on any modification of the policy itself.\n\nCypress' configurable CSP handling applies **only** to policies delivered via HTTP headers, not those delivered via a `<meta>` tag:\n\n*   [`experimentalCspAllowList`](#experimentalcspallowlist) operates exclusively on HTTP headers. It does nothing for a meta-tag CSP, so you cannot use the allow list to test a meta-delivered policy.\n*   The [`nonce` injection](#nonce-injection) described below likewise applies only to headers, never to `<meta>` tags. The \"scripts are loaded before the `<meta>` tag is parsed\" ordering is the only mechanism that makes meta-tag CSP work with Cypress.\n",
      "section": "app",
      "anchors": [
        "how-csp-is-delivered"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 237
    },
    {
      "id": "app/configure/content-security-policy#allowing-the-header-through-with-experimentalcspallowlist",
      "doc_id": "app/configure/content-security-policy",
      "heading": "Allowing the header through with experimentalCspAllowList",
      "heading_level": 2,
      "content_markdown": "## Allowing the header through with `experimentalCspAllowList`\n\nBy default Cypress strips the CSP header entirely. To keep it, so the browser enforces your policy during the test, configure the [`experimentalCspAllowList`](/llm/markdown/app/references/experiments.md#Experimental-CSP-Allow-List) option. It accepts three kinds of value:\n\n| Value | Behavior |\n| --- | --- |\n| `false` | _(default)_ Strip the entire CSP header. The policy is removed completely before the response reaches the browser. |\n| `true` | Keep the header, stripping only the directives that would interfere with Cypress (see below). |\n| `string[]` | Keep the header and the named directives, stripping everything else that Cypress would otherwise allow. |\n\nWhen using the array form, the values are validated and must be drawn from exactly these six allowable directives:\n\n*   `default-src`\n*   `child-src`\n*   `frame-src`\n*   `script-src`\n*   `script-src-elem`\n*   `form-action`\n\nAny other directive name is not accepted by the array. Regardless of which mode you choose, the [always-stripped directives](#always-stripped-directives) below are removed.\n\n`experimentalCspAllowList` cannot be changed at runtime and cannot be overridden per test. Calling `Cypress.config('experimentalCspAllowList', ...)` from within a spec has no effect, and there is no per-test override. Changing the value requires restarting the Cypress server (for example, by stopping and relaunching `cypress open` or `cypress run`) for the new setting to take effect.\n\n### Configuration examples\n\nStrip the entire CSP header (the default, equivalent to omitting the option):\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  // false is the default; the entire CSP header is stripped\n  experimentalCspAllowList: false,\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  // false is the default; the entire CSP header is stripped\n  experimentalCspAllowList: false,\n})\n```\n\nKeep the header, stripping only the directives that interfere with Cypress:\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  experimentalCspAllowList: true,\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  experimentalCspAllowList: true,\n})\n```\n\nKeep the header and only the named directives (each must be one of the six allowable values):\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'],\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'],\n})\n```\n\nEven with `experimentalCspAllowList` enabled, the CSP that reaches the browser is **not** byte-identical to your application's original policy. Cypress mutates three directives, as described in [Nonce injection](#nonce-injection) below. If your test asserts on the exact header value, account for this modification.\n",
      "section": "app",
      "anchors": [
        "allowing-the-header-through-with-experimentalcspallowlist"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 557
    },
    {
      "id": "app/configure/content-security-policy#configuration-examples",
      "doc_id": "app/configure/content-security-policy",
      "heading": "Configuration examples",
      "heading_level": 3,
      "content_markdown": "### Configuration examples\n\nStrip the entire CSP header (the default, equivalent to omitting the option):\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  // false is the default; the entire CSP header is stripped\n  experimentalCspAllowList: false,\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  // false is the default; the entire CSP header is stripped\n  experimentalCspAllowList: false,\n})\n```\n\nKeep the header, stripping only the directives that interfere with Cypress:\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  experimentalCspAllowList: true,\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  experimentalCspAllowList: true,\n})\n```\n\nKeep the header and only the named directives (each must be one of the six allowable values):\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'],\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'],\n})\n```\n\nEven with `experimentalCspAllowList` enabled, the CSP that reaches the browser is **not** byte-identical to your application's original policy. Cypress mutates three directives, as described in [Nonce injection](#nonce-injection) below. If your test asserts on the exact header value, account for this modification.\n",
      "section": "app",
      "anchors": [
        "configuration-examples"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 275
    },
    {
      "id": "app/configure/content-security-policy#always-stripped-directives",
      "doc_id": "app/configure/content-security-policy",
      "heading": "Always-stripped directives",
      "heading_level": 2,
      "content_markdown": "## Always-stripped directives\n\nRegardless of your `experimentalCspAllowList` configuration, Cypress always removes the following five directives from allowed CSP headers, because leaving them in place would prevent Cypress from functioning. They cannot be allowed through the allow list:\n\n| Stripped directive | Reason |\n| --- | --- |\n| `frame-ancestors` | Prevents Cypress from loading a test application into an iframe. |\n| `navigate-to` | Affects Cypress' ability to navigate to different URLs. |\n| `require-trusted-types-for` | Might prevent Cypress from rewriting the DOM. |\n| `sandbox` | Can restrict access to script and iframe functionality. |\n| `trusted-types` | Could cause Cypress injections to be marked as untrusted. |\n\nThis is a hard limitation: because these directives are stripped unconditionally and cannot be re-enabled, **you can never test the behavior of these five directives using Cypress**. If verifying them is a requirement, you will need to do so outside of Cypress.\n\nIf your application relies on any of these directives, be aware they will not be present in the policy delivered to the browser during a Cypress test run. See [Strip Minimum CSP Directives](/llm/markdown/app/references/experiments.md#Experimental-CSP-Allow-List) for the full breakdown.\n",
      "section": "app",
      "anchors": [
        "always-stripped-directives"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 252
    },
    {
      "id": "app/configure/content-security-policy#nonce-injection",
      "doc_id": "app/configure/content-security-policy",
      "heading": "Nonce injection",
      "heading_level": 2,
      "content_markdown": "## Nonce injection\n\nTo allow its injected `<script>` tags to execute under your policy, Cypress generates a random [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) for each response and appends a `'nonce-<value>'` source to the following directives of **every allowed CSP header**:\n\n*   `script-src-elem`\n*   `script-src`\n*   `default-src`\n\nA directive is only modified if it is present in the policy and allowed through your [`experimentalCspAllowList`](/llm/markdown/app/references/experiments.md#Experimental-CSP-Allow-List) configuration. The same `nonce` is also added to the `<script>` tags Cypress injects, so the browser permits them to run.\n\nBecause of this, the CSP that reaches the browser is **not** byte-identical to your application's original policy. If you are verifying your application's CSP implementation, be aware that these three directives are modified by Cypress and the delivered header will differ from the one your server originally sent.\n\nNonce injection applies only to CSP delivered via HTTP headers; it is never applied to a `<meta>` tag policy.\n",
      "section": "app",
      "anchors": [
        "nonce-injection"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 195
    },
    {
      "id": "app/configure/content-security-policy#interaction-with-code-modifying-options",
      "doc_id": "app/configure/content-security-policy",
      "heading": "Interaction with code-modifying options",
      "heading_level": 2,
      "content_markdown": "## Interaction with code-modifying options\n\nCypress options that rewrite your application's HTML or JavaScript can conflict with CSP directives that pin scripts by hash (for example a `script-src` value containing a `'sha256-...'` source). Because the hash is computed against the original code, any rewrite changes the bytes the browser hashes and the policy will reject the modified script.\n\nThis affects the following options when combined with `experimentalCspAllowList`:\n\n*   [`modifyObstructiveCode`](/llm/markdown/app/references/configuration.md#modifyObstructiveCode)\n*   [`experimentalModifyObstructiveThirdPartyCode`](/llm/markdown/app/references/experiments.md)\n\nIf you allow hash-based directives through `experimentalCspAllowList` while any of these code-modifying options are enabled, you can hit a mismatch between the original hashed value in the directive and the rewritten HTML or JS. If you see scripts being blocked, disable the code-modifying option or remove the hash-based directive from your allow list. See [Experimental CSP Allow List](/llm/markdown/app/references/experiments.md#Experimental-CSP-Allow-List) for more detail.\n",
      "section": "app",
      "anchors": [
        "interaction-with-code-modifying-options"
      ],
      "path": "/llm/json/chunked/app/configure/content-security-policy.json",
      "token_estimate": 176
    }
  ]
}