nonce
Allowlists inline scripts and styles under Content Security Policy. A unique server-generated token per request that prevents unauthorized code injection.
Overview
The nonce attribute allows specific inline <script> and <style> elements to execute under a Content Security Policy (CSP) that otherwise blocks all inline code. The server generates a unique, unpredictable token for each HTTP response, places it in both the CSP header and the allowed elements, and the browser only executes inline code whose nonce matches.
Without nonces (or hashes), a strict CSP must either block all inline scripts — breaking many applications — or use 'unsafe-inline', which defeats the purpose of CSP entirely.
How It Works
- The server generates a cryptographically random nonce value for each request.
- The server includes the nonce in the
Content-Security-Policyheader:script-src 'nonce-VALUE'. - The server adds
nonce="VALUE"to every trusted inline<script>and<style>tag in the response. - The browser compares each inline script/style nonce against the CSP header.
- Matching elements execute. Non-matching or nonce-less inline code is blocked.
- After parsing, the browser clears the nonce from the DOM so JavaScript cannot read it via
element.getAttribute('nonce').
<!-- Server generates a unique nonce for each HTTP response --><!-- CSP header: Content-Security-Policy: script-src 'nonce-abc123random' --> <!-- This inline script is allowed because its nonce matches the CSP header --><script nonce="abc123random"> document.title = 'Page loaded securely';</script> <!-- This inline style is allowed for the same reason --><!-- CSP header: Content-Security-Policy: style-src 'nonce-abc123random' --><style nonce="abc123random"> body { font-family: system-ui; }</style>
CSP Header Format
The nonce value in the CSP header must be prefixed with nonce- and wrapped in single quotes.
# CSP header allowing nonce-based inline scripts and stylesContent-Security-Policy: script-src 'nonce-rAnd0mV4luE' 'strict-dynamic'; style-src 'nonce-rAnd0mV4luE' # Combined with other directivesContent-Security-Policy: default-src 'self'; script-src 'nonce-rAnd0mV4luE' 'strict-dynamic'; style-src 'nonce-rAnd0mV4luE'; img-src 'self' https:
Server-Side Generation
The nonce must be generated server-side and must be unique per request. Use a cryptographically secure random number generator.
# Express.js — generate nonce per requestimport crypto from 'crypto'; app.use((req, res, next) => { const nonce = crypto.randomBytes(16).toString('base64'); res.locals.nonce = nonce; res.setHeader( 'Content-Security-Policy', `script-src 'nonce-${nonce}' 'strict-dynamic'; style-src 'nonce-${nonce}'` ); next();}); # In your template:# <script nonce="<%= nonce %>">...</script>
strict-dynamic
The 'strict-dynamic' CSP directive works with nonces to create a trust chain. Scripts loaded dynamically by a nonced script are automatically trusted, even without their own nonce attribute. This simplifies CSP for applications that load third-party scripts at runtime.
<!-- With 'strict-dynamic' in the CSP, scripts loaded BY a nonced script --><!-- are automatically trusted, even without their own nonce. --> <!-- CSP: script-src 'nonce-abc123' 'strict-dynamic' --><script nonce="abc123"> // This dynamically added script is trusted because the parent has a nonce const s = document.createElement('script'); s.src = '/js/analytics.js'; document.head.appendChild(s);</script>
CSP via Meta Tag
CSP can be set via a <meta> tag instead of an HTTP header. However, the HTTP header is preferred because it cannot be bypassed by injected HTML before the meta tag.
<!-- CSP can also be set via a meta tag (but HTTP header is preferred) --><meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abc123random'; style-src 'nonce-abc123random'" /> <script nonce="abc123random"> console.log('Allowed by meta CSP');</script>
Security Considerations
- Never hardcode nonces. A static nonce defeats the purpose — an attacker who discovers it can inject their own nonced scripts. Generate a new one per request.
- Never cache pages with nonces. If a CDN or browser cache serves a stale page, the nonce in the HTML will not match the nonce in the fresh CSP header, blocking all inline code. Use
Cache-Control: no-storeon pages with nonces. - DOM clearing protects you. After page load,
element.noncereturns the value via the IDL property, butelement.getAttribute('nonce')returns an empty string. This prevents injected scripts from reading the nonce out of existing elements. - Nonces do not protect external scripts. The
nonceattribute only affects inline code. Use'strict-dynamic'or explicit URL allowlists for external script sources.
Limitations
- Requires server-side rendering: Static sites served from a CDN cannot use nonces because there is no server to generate a unique value per request. Use CSP hashes instead.
- Caching conflicts: Pages with nonces must not be cached, since the nonce changes on every request.
- Not for event handlers: Nonces do not apply to inline event handler attributes like
onclick. Move event handling to nonced script blocks. - Debugging difficulty: CSP violations are logged in the console, but the error messages do not always clearly indicate a nonce mismatch versus a missing nonce.
See Also
integrity— verify external script and stylesheet contentscrossorigin— CORS mode required alongside SRIsandbox— iframe restrictions that complement CSP