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[] false;
  'connect-src'?: CSPSourceValue[] | false;
  'default-src'?: CSPSourceValue[] | false;
  'font-src'?: CSPSourceValue[] | false;
  'frame-src'?: CSPSourceValue[] | false;
  'img-src'?: CSPSourceValue[] | false;
  'manifest-src'?: CSPSourceValue[] | false;
  'media-src'?: CSPSourceValue[] | false;
  'object-src'?: CSPSourceValue[] | false;
  'prefetch-src'?: CSPSourceValue[] | false;
  'script-src'?: CSPSourceValue[] | false;
  'script-src-elem'?: CSPSourceValue[] | false;
  'script-src-attr'?: CSPSourceValue[] | false;
  'style-src'?: CSPSourceValue[] | false;
  'style-src-elem'?: CSPSourceValue[] | false;
  'style-src-attr'?: CSPSourceValue[] | false;
  'worker-src'?: CSPSourceValue[] | false;
  'base-uri'?: CSPSourceValue[] | false;
  'sandbox'?: CSPSandboxValue[] | false;
  'form-action'?: CSPSourceValue[] | false;
  'frame-ancestors'?: ("'self'" | "'none'" | string)[] | false;
  'report-uri'?: string[] | false;
  'report-to'?: string | false;
  'upgrade-insecure-requests'?: boolean;
} | false

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="base-uri 'none'; font-src 'self' https: data:; form-action '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' 'sha256-{{hash}}'; upgrade-insecure-requests;">
ℹ 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)
      exportToPresets: true // Export security headers to Nitro presets
    },
    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 reload 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.

As examplified below, Nuxt Security resolves the contentSecurityPolicy options substitutively at each nested 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: {
            'img-src': ["'self'", "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: {
            'img-src': ["'self'"] // ONLY self-hosted images can be loaded on /some-prefix/some-route/some-page
          }
        }
      }
    }
  }
})

Including External Scripts

You can include any external script (Google Analytics, Stripe, Cloudflare Turnstile, etc.) in your application without compromising the Strict CSP protection offered by Nuxt Security.

You will need to have default values for the 'strict-dynamic', nonce and ssg options.
If you change these default values, please refer to our Advanced Section on CSP for alternatives.
  • Since Nuxt 3.11, the easiest and universal way to include external scripts is via useScript
useScript('https://example.com/script.js')

Please see useScript for available options and usage.

  • Before Nuxt 3.11, you can also include your external scripts via useHead
useHead({
  script: [
    { 
      src: 'https://example.com/script.js',
    },
    {
      mode: 'client' // 'client' option is required in SSG mode
    }
  ] 
})
  • In all cases, you can also include external scripts directly in the HTML source
<script src="https://example.com/script.js" integrity="sha384-....." crossorigin="anonymous" />

When inserting scripts directly in HTML, you will need to provide an integrity hash if you are using SSG mode. Many standard scripts (e.g. Google Analytics) do not provide an integrity hash, therefore this method is not compatible with SSG.