A Content Security Policy (Content Security Policy) that forbids inline scripts is the strongest defense against XSS, but real pages have inline code: a configuration object, an analytics snippet, a theme switch that must run before first paint. The nonce attribute ("number used once") approves those specific blocks. The server generates a fresh random value for every response and prints it both in the policy, as 'nonce-VALUE', and on each approved <script> or <style>. Injected markup cannot know this response's value, so it does not run. Use at least 128 bits from a cryptographically secure generator, base64-encoded, new on every page load; in Node.js 2,131 that is crypto.randomBytes(16).toString('base64'). A fixed nonce in a template is no better than 'unsafe-inline', and neither is a nonced page replayed from a CDN cache, so send such pages with Cache-Control: no-store or private.
Frameworks automate this: Next.js 10,514 generates the nonce in its request proxy, extracts it from the Content-Security-Policy header while rendering and adds it to its own scripts. The demo uses a <meta> policy so it works as a static file. It contains a nonced script, a plain inline script, a guessed nonce and an onclick.
<!doctype html>
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-Q2hhcHRlcjJub25jZQ'">
<ul id="out" style="font: 15px system-ui"></ul>
<script nonce="Q2hhcHRlcjJub25jZQ">
const say = (t) => out.insertAdjacentHTML('beforeend', `<li>${t}</li>`);
say('Nonced script ran. getAttribute("nonce") returns "' +
document.currentScript.getAttribute('nonce') + '"');
document.addEventListener('securitypolicyviolation', (e) =>
say(`Blocked (${e.effectiveDirective}) at line ${e.lineNumber}`));
</script>
<script>say('Injected script ran');</script>
<script nonce="guess123">say('Guessed nonce ran');</script>
<button onclick="say('Inline handler ran')" id="b">Button</button>
<script nonce="Q2hhcHRlcjJub25jZQ">b.click();</script>
Nonces do not cover inline event handlers or javascript: URLs; move that code into nonced scripts with addEventListener(). For static pages that cannot be generated per request, list the SHA-256 hash of each inline script instead ('sha256-...', the same base64 format as integrity); Chrome 1 's console message for a blocked inline script includes its hash, ready to copy into the policy.