Content Security Policy: nonce and hash
Write a strict CSP with nonces for inline scripts, hashes for known snippets, and report-uri to monitor violations in production.
Content Security Policy: nonce and hash is a free Frontend Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
CSP Recap
CSP is an HTTP header that whitelists what the browser is allowed to load — scripts, styles, images, fonts, frames, connections. Defence in depth: even if XSS gets past your filters, CSP often stops the payload.
CSP Header Anatomy
Each directive lists allowed sources. 'self' means same-origin. Specific URLs are allowed too.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
font-src 'self' https://fonts.gstatic.com;
frame-ancestors 'none';
base-uri 'self';Common Directives
default-src: fallback for everything. script-src: JavaScript. style-src: CSS. img-src: images. connect-src: fetch/XHR/WebSocket. font-src: fonts. frame-ancestors: who can iframe you (clickjacking protection).
'unsafe-inline' — The Common Hole
Many sites add 'unsafe-inline' to allow inline <script> tags and onclick attributes. This defeats CSP's main purpose — XSS payloads can run inline. Replace with nonces or hashes.
Nonces — One-Time Allowlist
Generate a random nonce per request. Tag legitimate inline scripts with the nonce. Browser allows only scripts with matching nonces.
// Server (Express middleware):
import crypto from 'crypto';
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy',
`script-src 'nonce-${res.locals.nonce}' 'strict-dynamic'`
);
next();
});
// Template:
<script nonce="<%= nonce %>">window.config = {...};</script>'strict-dynamic'
Combine nonce with 'strict-dynamic': trusted scripts (those with the nonce) can load additional scripts. Eliminates the need to list every script source URL. The current CSP best practice.
Hashes — Static Allowlist
For known-fixed inline scripts (e.g. your build always emits the same bootstrap snippet), compute its SHA-256 and add it as a hash source. No per-request nonce needed.
// Hash of: console.log('hi');
Content-Security-Policy: script-src 'sha256-XwCNuB+/RUgPlAACI+yHrUKqUsm4zlpGV/Q8tEUx0Q4='Inline Style Hashes
Same trick for inline <style> tags — compute their hash and add it to style-src. Better than 'unsafe-inline' for CSS.
CSP Report Mode
Use Content-Security-Policy-Report-Only to test a policy without enforcing. Violations are reported to your endpoint but don't block anything. Great for rolling out CSP gradually.
Content-Security-Policy-Report-Only:
default-src 'self';
report-uri /csp-violations
// /csp-violations receives POSTs like:
{
"csp-report": {
"document-uri": "https://example.com/",
"violated-directive": "script-src 'self'",
"blocked-uri": "https://evil.com/x.js"
}
}report-to (Modern)
report-to + Reporting-Endpoints is the modern replacement for report-uri. Same data, more structured.
Reporting-Endpoints: csp="/csp-reports"
Content-Security-Policy: script-src 'self'; report-to cspFramework Integrations
Next.js: set CSP via middleware.ts or next.config.js headers. Nuxt: nuxt-security module. Vite: configure dev server headers; production via your host (Vercel, Netlify) headers config.
// next.config.js
module.exports = {
async headers() {
return [{
source: '/(.*)',
headers: [{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'strict-dynamic'"
}]
}];
}
};Testing CSP
Open DevTools → Console: CSP violations log there. Use https://csp-evaluator.withgoogle.com/ to grade your policy. Report-only mode in production first; enforce after fixing all violations.
Quick Check
Why is 'unsafe-inline' in a script-src directive considered a CSP weakness?
Recap: CSP Best Practice
Set Content-Security-Policy header with directives per resource type. Avoid 'unsafe-inline' — use nonces + 'strict-dynamic' or SHA hashes. Set frame-ancestors 'none' (or 'self'). Test in Report-Only mode first. Send violations to /csp-violations (or use modern report-to). Frameworks support CSP via middleware or config headers.
Frequently asked questions
Is the “Content Security Policy: nonce and hash” lesson free?
Yes — the full text of “Content Security Policy: nonce and hash” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.
What will I learn in “Content Security Policy: nonce and hash”?
Write a strict CSP with nonces for inline scripts, hashes for known snippets, and report-uri to monitor violations in production. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Frontend Academy?
No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Content Security Policy: nonce and hash” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Frontend Academy lesson?
Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- XSS Prevention: Output Encoding CSP
- CSRF: SameSite Cookies and Tokens
- Content Security Policy: nonce and hash
- OAuth Flows from the Frontend