Express
Express has no opinion about security headers, so almost everyone reaches for Helmet, which sets a stack of headers including a default CSP. That default is deliberately strict, and it is usually the first thing that breaks an existing app - which is exactly why you want report-only first.
What Helmet gives you
- A default
Content-Security-Policyapplied as soon as youapp.use(helmet()), includingdefault-src 'self',script-src 'self',object-src 'none'andupgrade-insecure-requests. - Custom directives passed as a nested
directivesobject. - Directive values that can be functions receiving the request and response, which is how per-request nonces get in.
- A
reportOnlyflag that swaps the header forContent-Security-Policy-Report-Only.
Setting the header
Your URL comes from the Setup page:
app.use(
helmet({
contentSecurityPolicy: {
reportOnly: true,
directives: {
"default-src": ["'self'"],
"script-src": ["'self'"],
"report-uri": ["https://abc123.report-uri.com/r/d/csp/reportOnly"],
},
},
}),
);
Adding a nonce
Generate the nonce before Helmet runs, then read it back in a directive function:
import crypto from 'node:crypto'
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64')
next()
})
app.use(
helmet({
contentSecurityPolicy: {
directives: {
"script-src": ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
},
},
}),
)
Then render nonce="<%= nonce %>" on your inline scripts.
Things to watch for
- Custom
directivesreplace Helmet's defaults for the directives you name, and Helmet merges the rest. Check the header you actually emit rather than assuming your object is additive. - Middleware order matters: the nonce middleware has to run before Helmet, or the directive function reads
undefined. - Helmet's defaults change between major versions. Pin the version and re-check the emitted header after an upgrade, rather than discovering it from production reports.
Official documentation
Helmet - Content Security Policy
Start Monitoring with Report URI
Already using us for CSP? Find your report-uri value on Setup, or view your CSP reports.
New to Report URI? Create an account and grab your report-uri value from the Setup page.