Content Security Policy Middleware for Express
express-csp-header is an Express.js middleware designed to streamline the implementation of Content-Security-Policy (CSP) headers in web applications. It wraps the functionality of the `csp-header` package, providing an Express-specific interface. Currently at stable version 6.3.1, the package maintains an active release cadence, frequently releasing patches and minor features. Key differentiators include its integration with Express's middleware system, providing convenient constants like `SELF`, `INLINE`, `NONCE`, and `TLD` for dynamic policy generation. It also features automatic Top-Level Domain (TLD) parsing and custom CSP string processing capabilities. The library facilitates managing both `Content-Security-Policy` and `Reporting-Endpoints` headers, making it easier to implement robust web security measures against attacks like XSS. It requires Node.js version 18 or higher.
Common errors
-
Refused to load the script '...' because it violates the following Content Security Policy directive: "script-src 'self'".
cause A script (or other resource) is attempting to load from an origin or with attributes (e.g., inline) not permitted by the `script-src` (or other relevant) CSP directive.fixInspect your browser's developer console for the exact CSP violation message. Adjust the corresponding CSP directive in `express-csp-header` to include the blocked source (e.g., `'unsafe-inline'`, `'unsafe-eval'`, `NONCE`, or specific domains). For inline scripts/styles, use `NONCE` or `INLINE` (with caution). -
Cannot set headers after they are sent to the client
cause The `express-csp-header` middleware is being applied after a response has already been partially or fully sent by another middleware or route handler.fixEnsure `app.use(expressCspHeader(...))` is placed early in your Express middleware chain, typically before any routes or other middleware that might send responses. -
TypeError: Cannot read properties of undefined (reading 'nonce')
cause You are trying to access `req.nonce` but either `NONCE` was not included in your `script-src` or `style-src` directives, or the middleware was not correctly applied before the route handler.fixVerify that `NONCE` is included in the relevant `script-src` or `style-src` directives passed to `expressCspHeader`. Also, ensure the middleware is active for the route where `req.nonce` is accessed.
Warnings
- breaking Version 6.0.0 introduced a breaking change where the `"'none'"` value is no longer implicitly removed for merged directives that have multiple declarations. This can alter the final CSP string if you rely on such merging behavior.
- breaking Version 5.0.0 increased the minimum supported Node.js version to 10. The current package version (6.x) requires Node.js `>=18`. Using this package with older Node.js versions will result in runtime errors.
- breaking Version 5.0.0 deprecated and subsequently removed the `extend` option in favor of `presets`. If you were using `extend` for combining CSP rules, your configuration will no longer work.
- breaking Version 3.0.0 changed `expressCspHeader` from a default export to a named export. This means `const expressCspHeader = require('express-csp-header')` no longer works and requires destructuring.
- gotcha `Reporting-Endpoints` is no longer recommended by modern browsers in favor of `Content-Security-Policy-Report-Only` (using `report-uri`) or a dedicated `Report-To` header. While `express-csp-header` supports `report-to` for `csp-header`, users should be aware of current browser recommendations and policy header best practices.
- gotcha Improperly configured Content Security Policy directives can lead to legitimate resources being blocked, resulting in a broken or unusable website. This is especially true for directives like `script-src` and `style-src` if `unsafe-inline` or appropriate nonces/hashes are not used for inline content.
Install
-
npm install express-csp-header -
yarn add express-csp-header -
pnpm add express-csp-header
Imports
- expressCspHeader
const expressCspHeader = require('express-csp-header'); // Incorrect for named export access const { expressCspHeader } = require('express-csp-header'); // Correct CJS, but prefer ESMimport { expressCspHeader } from 'express-csp-header'; - SELF
const SELF = 'self'; // Hardcoding constant value; less maintainable
import { SELF } from 'express-csp-header'; - NONCE
import { Nonce } from 'express-csp-header';import { expressCspHeader, NONCE } from 'express-csp-header';
Quickstart
import express from 'express';
import { expressCspHeader, INLINE, NONE, SELF, NONCE } from 'express-csp-header';
const app = express();
const port = process.env.PORT || 3000;
app.use(expressCspHeader({
directives: {
'default-src': [SELF, 'somehost.com'],
'script-src': [SELF, INLINE, NONCE],
'style-src': [SELF, 'mystyles.net', INLINE],
'img-src': ['data:', 'images.com'],
'worker-src': [NONE],
'block-all-mixed-content': true,
'report-uri': ['https://example.com/csp-report-endpoint']
},
reportOnly: false // Set to true to test CSP without blocking resources
}));
app.get('/', (req, res) => {
// The generated nonce is available on req.nonce if NONCE is used in directives
const scriptNonce = req.nonce || '';
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>CSP Example</title>
<style nonce="${scriptNonce}">body { font-family: sans-serif; }</style>
</head>
<body>
<h1>Hello, CSP!</h1>
<p>This page demonstrates CSP with nonce.</p>
<script nonce="${scriptNonce}">
console.log('Script executed with nonce:', '${scriptNonce}');
// An intentionally blocked inline script without nonce (will appear in console if not allowed by CSP)
// setTimeout(() => alert('This might be blocked!'), 100);
</script>
<img src="https://images.com/example.jpg" alt="Example Image">
</body>
</html>
`);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
console.log('Check browser developer tools for CSP header and potential violations.');
});