Skip to content

feat: add nonce support for inline style and script tags in CSP#3709

Merged
bartlomieju merged 6 commits into
mainfrom
feat/csp-style-nonce
Mar 29, 2026
Merged

feat: add nonce support for inline style and script tags in CSP#3709
bartlomieju merged 6 commits into
mainfrom
feat/csp-style-nonce

Conversation

@bartlomieju

Copy link
Copy Markdown
Contributor

Supersedes #1787.

Summary

  • Auto-inject nonce attribute onto inline <script> and <style> tags during server rendering
  • Add useNonce option to the CSP middleware that replaces 'unsafe-inline' with per-request 'nonce-{value}' directives
  • Expose render nonce via X-Fresh-Nonce response header (internal, stripped by CSP middleware)

Usage

app.use(csp({ useNonce: true }));

This locks down the CSP policy so only Fresh-rendered inline scripts/styles are allowed. Each request gets a unique nonce.

How it works

  1. During SSR, the Preact vnode hook injects nonce={RENDER_STATE.nonce} onto all <script> and <style> elements (unless they already have an explicit nonce)
  2. After rendering, ctx.render() sets X-Fresh-Nonce on the response
  3. The CSP middleware (when useNonce: true) reads the nonce, replaces 'unsafe-inline' with 'nonce-{value}' in script-src and style-src, and strips the internal header

Test plan

  • Nonce replaces unsafe-inline in CSP header (script-src and style-src)
  • Nonce injected on inline <script> tags
  • Nonce injected on inline <style> tags
  • Non-rendered responses (API routes) fall back to unsafe-inline
  • Unique nonce per request
  • Existing explicit nonce attribute preserved
  • All 3 original CSP tests still pass
  • deno fmt and deno lint clean

🤖 Generated with Claude Code

bartlomieju and others added 3 commits March 26, 2026 18:11
- Auto-inject nonce attribute onto inline <script> and <style> tags
  during server rendering (preact_hooks.ts vnode hook)
- Expose render nonce via X-Fresh-Nonce response header (context.ts)
- Add useNonce option to CSP middleware that replaces 'unsafe-inline'
  with nonce-based directives per request
- Existing explicit nonce attributes on tags are preserved
- Non-rendered responses (API routes) fall back to unsafe-inline

Supersedes #1787.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bartlomieju and others added 3 commits March 29, 2026 20:35
- Replace X-Fresh-Nonce header with a Symbol property on the Response
  object so the nonce never leaks when CSP middleware is absent
- Use replaceAll instead of replace for 'unsafe-inline' substitution
- Handle unsafe-inline in default-src, script-src-elem, style-src-elem,
  and style-src-attr directives (not just script-src and style-src)
- Document that explicit nonce attributes on tags will be blocked by
  the CSP header (which only contains the Fresh-generated nonce)
- Add tests for nonce non-leakage and default-src replacement

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bartlomieju bartlomieju merged commit dc23248 into main Mar 29, 2026
9 checks passed
@bartlomieju bartlomieju deleted the feat/csp-style-nonce branch March 29, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant