DEV Community

Cover image for How to Safely Allow Inline Scripts Without Breaking Security with CSP Nonce
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

How to Safely Allow Inline Scripts Without Breaking Security with CSP Nonce

When building modern web applications, security is not optional. One of the most important protections you can add is a Content Security Policy (CSP).

But here’s the catch:

👉 CSP often blocks inline scripts and styles — which can break your app.

So how do you keep your app secure without disabling useful features?

That’s where CSP nonce comes in - it allows you to safely execute inline code without opening security holes.

In this article, we’ll explore:

  • What CSP nonce is
  • What problem it solves
  • How to implement it
  • How it works automatically in Nuxt with nuxt-security
  • Best practices and common pitfalls

Let’s dive in.

🤔 What Is CSP Nonce?

A nonce (short for number used once) is a unique, random value generated for each request.

It is used in CSP to explicitly allow trusted inline scripts or styles.

Example:

<script nonce="abc123">
  console.log('Secure inline script')
</script>
Enter fullscreen mode Exit fullscreen mode

And in your HTTP headers:

Content-Security-Policy: script-src 'nonce-abc123'
Enter fullscreen mode Exit fullscreen mode

The browser will only execute scripts that have a matching nonce.

CSP nonce is a whitelist mechanism:

  • Only scripts with the correct nonce are allowed
  • Everything else is blocked
  • The nonce changes on every request

This makes it extremely effective against XSS (Cross-Site Scripting) attacks.

CSP nonce is commonly used for:

  1. SSR frameworks - Injecting initial state, Hydration scripts
  2. Analytics / tracking snippets - Inline scripts required by providers
  3. Critical inline scripts - Small scripts needed before app bootstraps

🟢 What Problem Does CSP Nonce Solve?

Without nonce, you usually face a trade-off:

❌ Allow inline scripts (unsafe)

Content-Security-Policy: script-src 'unsafe-inline'
Enter fullscreen mode Exit fullscreen mode

Problem:

  • Opens the door to XSS attacks
  • Any injected script can run

❌ Block inline scripts completely

Content-Security-Policy: script-src 'self'
Enter fullscreen mode Exit fullscreen mode

Problem:

  • breaks inline event handlers
  • breaks injected scripts (SSR hydration, state)
  • breaks some frameworks

🟢 How to Implement CSP Nonce

The process is relatively simple but let's break it down and explain each step individually.

Step 1: Generate a nonce per request

Example (Node.js):

import crypto from 'crypto'

function generateNonce() {
  return crypto.randomBytes(16).toString('base64')
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Add it to response headers

const nonce = generateNonce()

res.setHeader(
  'Content-Security-Policy',
  `script-src 'nonce-${nonce}'`
)
Enter fullscreen mode Exit fullscreen mode

Step 3: Inject it into your HTML

<script nonce="{{nonce}}">
  window.__INITIAL_STATE__ = {}
</script>
Enter fullscreen mode Exit fullscreen mode

⚠️ Critical rule

The nonce in the header and HTML must match exactly. Otherwise, script will be blocked and app may break silently.

🟢 CSP Nonce in Nuxt (with nuxt-security)

If you’re using Nuxt, things get much easier thanks to nuxt-security module that can:

  • Automatically generate nonce per request
  • Inject it into CSP headers
  • Attach it to scripts/styles

It makes it much easier to work with CSP nonces in Nuxt. Let's take a look at the following configuration:

export default defineNuxtConfig({
  modules: ['nuxt-security'],
  security: {
    headers: {
      contentSecurityPolicy: {
        'script-src': [
          "'self'",
          "'nonce-{{nonce}}'"
        ]
      }
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

What happens automatically:

  • Nuxt generates a nonce per request
  • Replaces {{nonce}} in headers
  • Applies nonce to inline scripts

You don’t need to manually wire everything

You can read more here:
https://nuxt-security.vercel.app/

🟢 Common Mistakes

Let's take a look at the list of common mistakes to understand what to look for:

  1. ❌ Reusing the same nonce - Nonce must be unique per request and cryptographically random
  2. ❌ Forgetting to apply nonce in HTML - if you only set CSP header the scripts will still be blocked
  3. ❌ Mixing nonce with unsafe-inline - script-src 'unsafe-inline' 'nonce-abc'
  4. ❌ Caching issues - if HTML is cached nonce may not match header

🧪 Best Practices

  • Generate nonce per request
  • Use secure randomness (crypto)
  • Never reuse nonce
  • Avoid unsafe-inline
  • Use frameworks/tools (like nuxt-security)
  • Test CSP in report-only mode first
  • Monitor browser console for CSP violations

📖 Learn more

If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:

Vue School Link

It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉

🧪 Advance skills

A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you're advancing your career or switching paths, it's a smart step toward success.

Check out Certificates.dev by clicking this link or by clicking the image below:

Certificates.dev Link

Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more!

✅ Summary

CSP nonce is a powerful mechanism that allows you to safely use inline scripts while maintaining strong security.

In this article, you learned:

  • What CSP nonce is and how it works
  • What problem it solves (security vs flexibility)
  • How to implement it step by step
  • How Nuxt + nuxt-security handle it automatically
  • Common mistakes and best practices

Take care!
And happy coding as always 🖥️

Top comments (0)