Skip to content
MDView as Markdown

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-Policy applied as soon as you app.use(helmet()), including default-src 'self', script-src 'self', object-src 'none' and upgrade-insecure-requests.
  • Custom directives passed as a nested directives object.
  • Directive values that can be functions receiving the request and response, which is how per-request nonces get in.
  • A reportOnly flag that swaps the header for Content-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 directives replace 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.

Start your free trial