Content Security Policy

Enabled Prevent unwanted content from being injected in your app.


Content Security Policy (CSP) helps prevent unwanted content from being injected/loaded into your webpages. This can mitigate cross-site scripting (XSS) vulnerabilities, clickjacking, formjacking, malicious frames, unwanted trackers, and other web client-side attacks.

ℹ Read more about this header here.

Usage

This header is enabled by default but you can change its behavior like following.

export default defineNuxtConfig({  // Global  security: {    headers: {      contentSecurityPolicy: <OPTIONS>,    },  },  // Per route  routeRules: {    '/custom-route': {      security: {        headers: {          contentSecurityPolicy: <OPTIONS>,        },      },    }  }})

You can also disable this header by contentSecurityPolicy: false.

Default value

By default, Nuxt Security will set following value for this header:

Content-Security-Policy: base-uri 'none'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; script-src 'self' https: 'unsafe-inline' 'strict-dynamic' 'nonce-{{nonce}}'; upgrade-insecure-requests;

Available values

The contentSecurityPolicy header can be configured with following values.

contentSecurityPolicy: {  'child-src'?: CSPSourceValue[] | string | false;  'connect-src'?: CSPSourceValue[] | string | false;  'default-src'?: CSPSourceValue[] | string | false;  'font-src'?: CSPSourceValue[] | string | false;  'frame-src'?: CSPSourceValue[] | string | false;  'img-src'?: CSPSourceValue[] | string | false;  'manifest-src'?: CSPSourceValue[] | string | false;  'media-src'?: CSPSourceValue[] | string | false;  'object-src'?: CSPSourceValue[] | string | false;  'prefetch-src'?: CSPSourceValue[] | string | false;  'script-src'?: CSPSourceValue[] | string | false;  'script-src-elem'?: CSPSourceValue[] | string | false;  'script-src-attr'?: CSPSourceValue[] | string | false;  'style-src'?: CSPSourceValue[] | string | false;  'style-src-elem'?: CSPSourceValue[] | string | false;  'style-src-attr'?: CSPSourceValue[] | string | false;  'worker-src'?: CSPSourceValue[] | string | false;  'base-uri'?: CSPSourceValue[] | string | false;  'sandbox'?: CSPSandboxValue[] | string | false;  'form-action'?: CSPSourceValue[] | string | false;  'frame-ancestors'?: ("'self'" | "'none'" | string)[] | string | false;  'navigate-to'?: ("'self'" | "'none'" | "'unsafe-allow-redirects'" | string)[] | string | false;  'report-uri'?: string[] | string | false;  'report-to'?: string | false;  'upgrade-insecure-requests'?: boolean;} | false

Array and String syntaxes

CSPSourceValue type

CSPSandboxValue type

Strict CSP

Nuxt Security helps you increase the security of your site by enabling Strict CSP support for both SSR and SSG applications.

For further reading about Strict CSP and how to handle specific cases, please consult our Adanced Section about Strict CSP

  • For SSR applications, Nuxt Security implements strict CSP via nonces. A one-time cryptographically-generated random nonce is generated at runtime by the server for each request of a page.
  • For SSG applications, Nuxt Security implements strict CSP via hashes. At static build-time, Nuxt Security computes the SHA hashes of the elements that are allowed to execute on your site.

By default, Strict CSP will be enabled on your site. The following default configuration options are used:

export default defineNuxtConfig({  security: {    nonce: true, // Enables HTML nonce support in SSR mode    ssg: {      meta: true, // Enables CSP as a meta tag in SSG mode      hashScripts: true, // Enables CSP hash support for scripts in SSG mode      hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended)    },    headers: {      contentSecurityPolicy: {        'script-src': [          "'self'",  // Fallback value, will be ignored by most modern browsers (level 3)          "https:", // Fallback value, will be ignored by most modern browsers (level 3)          "'unsafe-inline'", // Fallback value, will be ignored by almost any browser (level 2)          "'strict-dynamic'", // Strict CSP via 'strict-dynamic', supported by most modern browsers (level 3)          "'nonce-{{nonce}}'" // Enables CSP nonce support for scripts in SSR mode, supported by almost any browser (level 2)        ],        'style-src': [          "'self'", // Enables loading of stylesheets hosted on same origin          "https:", // For increased security, replace by the specific hosting domain or file name of your external stylesheets          "'unsafe-inline'" // Recommended default for most Nuxt apps        ],        'img-src': ["'self'", "data:"], // Add relevant https://... sources if you load images from external sources         'font-src': ["'self'", "https:", "data:"], //  For increased security, replace by the specific sources for fonts        'base-uri': ["'none'"],        'object-src': ["'none'"],        'script-src-attr': ["'none'"],        'form-action': ["'self'"],        'frame-ancestors': ["'self'"],        'upgrade-insecure-requests': true      }    },    sri: true  }})

Server Side Rendering (SSR)

Nuxt Security provides first-class support of SSR apps via 'strict-dynamic' and nonces.

In SSR mode, Strict CSP is enabled when you set the nonce option and the "'nonce-{{nonce}}'" placeholders:

export default defineNuxtConfig({  // Global  security: {    nonce: true, // Enables HTML nonce support in SSR mode  },  headers: {    contentSecurityPolicy: {      'script-src': [        "'strict-dynamic'", // Modify with your custom CSP sources        "'nonce-{{nonce}}'" // Enables CSP nonce support for scripts in SSR mode, supported by almost any browser (level 2)      ]    }  },  // Per route  routeRules: {    '/custom-route': {      security: {        nonce: false,        headers: {          contentSecurityPolicy: {            'script-src': "self 'unsafe-inline'"          },        },      },    }  }})
  • nonce: Set this option to true to parse all <script>, <link> and <style> tags in your application and add the nonce value to these. The module parses all inline elements as well as all external resources.
  • "'nonce-{{nonce}}'" placeholder: Include this value in any individual policy that you want to be governed by nonce.
Our default recommendation is to avoid using the "'nonce-{{nonce}}'" placeholder on style-src policy.
⚠ This is because Nuxt's mechanism for Client-Side hydration of styles could be blocked by CSP in that case.
For further discussion and alternatives, please refer to our Advanced Section on Strict CSP.

Note: Nonce only works for SSR. The nonce option and the "'nonce-{{nonce}}'" placeholders are ignored when you build your app for SSG via nuxi generate.

Static Site Generation (SSG)

This module is meant to work with SSR apps, but you can also use this module in SSG apps where you will get a Content Security Policy (CSP) support via <meta http-equiv> tag.

This will result in following code being added to your static app <head> tag:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
ℹ Read more about this here.

For SSG apps, Strict CSP is enabled when you set the ssg and sri options:

export default defineNuxtConfig({  // Global  security: {    ssg: {      meta: true, // Enables CSP as a meta tag in SSG mode      hashScripts: true, // Enables CSP hash support for scripts in SSG mode      hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended)    },    sri: true,    headers: {      contentSecurityPolicy: {        'script-src': [          "'strict-dynamic'", // Modify with your custom CSP sources          // The nonce-{{nonce}} placeholder is not required and will be ignored in SSG mode        ]      }    }  },  // Per route  routeRules: {    '/custom-route': {      security: {        ssg: false,        sri: false,        headers: {          contentSecurityPolicy: {            'script-src': "self 'unsafe-inline'"          },        },      },    }  }})

Nuxt Security will generate script hashes and style hashes for you according to the ssg option:

  • hashScripts: Set this option to true to parse all inline scripts as well as all external scripts. Nuxt-Security will compute the hashes of inline scripts and find the integrity attributes of external scripts, and will add them to your script-src policy.
  • hashStyles: Set this option to true to parse all inline styles as well as all external styles. Nuxt-Security will compute the hashes of inline styles and find the integrity attributes of external styles, and will add them to your style-src policy.
Our default recommendation is to avoid setting the ssg: hashStyles option to true.
⚠ This is because Nuxt's mechanism for Client-Side hydration of styles could be blocked by CSP in that case.
For further discussion and alternatives, please refer to our Advanced Section on CSP.
Nuxt Security will automatically calculate the integrity attributes of all your bundled assets if you set the sri option to true. For unbundled assets, you may need to set the integrity attribute manually.
Please see below our section on Integrity Hashes For SSG

Note: Hashes only work for SSG. The ssg options are ignored when you build your app for SSR via nuxi build.

Hot reloading during development

If you have enabled nonce-{{nonce}} on style-src, you will need to disable it in order to allow hot reloading during development.

export default defineNuxtConfig({  security: {    nonce: true,    headers: {      contentSecurityPolicy: {        'style-src': process.env.NODE_ENV = 'development' ?         ["'self'", "'unsafe-inline'"] :        ["'self'", "'unsafe-inline'", "nonce-{{nonce}}"]      }    }  }})

Note that this is not necessary if you use our default configuration settings.

Per-route configuration

All Content Security Policy options can be defined on a per-route level.

export default defineNuxtConfig({  // Global  security: {    headers: {      contentSecurityPolicy: {        'img-src': false // By default, no images can be loaded      }    }  }  // Per route  routeRules: {    '/some-prefix/**': {      security: {        headers: {          contentSecurityPolicy: {            'img-src': ["'self'"] // Self-hosted images can be loaded for routes beginning with /some-prefix/          }        }      }    },    '/some-prefix/some-route/**': {      security: {        headers: {          contentSecurityPolicy: { // With array syntax : additive            'img-src': ["https:"]  // Self-hosted AND https: images can be loaded for routes beginning with /some-prefix/some-route/          }        }      }    },    '/some-prefix/some-route/some-page': {      security: {        headers: {          contentSecurityPolicy: { // With string syntax : substitutive            'img-src': 'self'      // ONLY self-hosted images can be loaded on /some-prefix/some-route/some-page          }        }      }    }  }})

Nuxt Security resolves the contentSecurityPolicy options using the native Nitro router strategy:

  • Additive merging with the array syntax: If you write your rules with the array syntax (e.g. "img-src": ["'self'", "https:"]), the new route policies will be added to the policies defined for higher-level routes. Use this strategy if you need to add specific policy values to your route without deleting the existing ones.
  • Substitutive merging with the string syntax: If you write your rules with the string syntax (e.g. "img-src": "'self' https:"), the new route policies will be substituted to the policies defined for higher-level routes. Use this strategy if you need to delete existing policies before setting your specific route policies.

Nonces for SSR

For SSR apps, if you use 'strict-dynamic' in your script-src policy, each of your external scripts will need to be whitelisted by nonce.

Fortunately, Nuxt Security will include the nonce in all your relevant HTML resources by default.

You can easily add your external scripts to your HTML document with the useHead composable:

useHead({  script: [    { src: 'https://example.com/script.js' }    ]   }, {     mode: 'server'     // Set the mode to 'server' if you want to avoid re-loading the script on the client    // Set the mode to 'client' if you want client-side only loading    // see: https://github.com/unjs/unhead/issues/136   })

If you are unwilling or unable to use useHead, you can also directly insert tags into the DOM via document.createElement():

<script setup>onMounted(() => {  const script = document.createElement('script')  script.src = 'loader.js'  document.head.appendChild(script)})</script>

Integrity Hashes For SSG

For SSG apps, if you use 'strict-dynamic' in your script-src policy, each of your external scripts will need to carry an integrity attribute.

This is a mandatory requirement of CSP Level 3.

You can easily add integrity values to your scripts with the useHead composable:

useHead({  script: [    {       src: 'https://example.com/script.js',      crossorigin: 'anonymous',      integrity: 'sha384-.....' // Insert the integrity hash here    }  ] })

If you insert scripts manually in your app, you can also include their integrity attribute manually

<script src="https://example.com/script.js" integrity="sha384-....." crossorigin="anonymous" />
⚠ For further information about integrity hashes with SSG, please refer to our Advanced Section on CSP for alternatives.