express-csp-header vs helmet
HTTP Security Headers in Express.js Applications
express-csp-headerhelmetSimilar Packages:

HTTP Security Headers in Express.js Applications

express-csp-header and helmet are both npm packages designed to help Express.js applications set HTTP security headers that protect against common web vulnerabilities. helmet is a comprehensive middleware that sets multiple security-related headers by default, including Content Security Policy (CSP), XSS Protection, HSTS, and more. express-csp-header is a focused utility that only handles Content Security Policy headers, offering a programmatic way to define CSP directives without writing raw header strings.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
express-csp-header02457.4 kB1a month agoWTFPL
helmet010,665104 kB4a year agoMIT

Securing Express Apps: express-csp-header vs helmet

When building web applications with Express.js, setting proper HTTP security headers is non-negotiable. Two common tools for this are express-csp-header and helmet. While both can configure Content Security Policy (CSP), they differ significantly in scope, maintenance, and philosophy. Let’s break down how they work in practice.

🛡️ Scope: Focused Tool vs Full Security Suite

express-csp-header does one thing: generate and set the Content-Security-Policy header. It provides a clean API to define CSP directives using JavaScript objects instead of raw strings.

// express-csp-header: Only sets CSP
import express from 'express';
import csp from 'express-csp-header';

const app = express();

app.use(csp({
  policies: {
    'default-src': [csp.SELF],
    'script-src': [csp.SELF, 'https://trusted.cdn.com'],
    'style-src': [csp.SELF, 'https://fonts.googleapis.com']
  }
}));

helmet sets multiple security headers out of the box, including CSP, but also:

  • X-Content-Type-Options
  • X-Frame-Options
  • X-XSS-Protection
  • Strict-Transport-Security
  • Referrer-Policy
  • And more

You can enable all defaults or pick specific ones:

// helmet: Sets many headers, including CSP
import express from 'express';
import helmet from 'helmet';

const app = express();

// Enable all recommended headers
app.use(helmet());

// Or just CSP with custom config
app.use(helmet.contentSecurityPolicy({
  directives: {
    'default-src': ["'self'"],
    'script-src': ["'self'", 'https://trusted.cdn.com'],
    'style-src': ["'self'", 'https://fonts.googleapis.com']
  }
}));

🔧 Configuration Style: Object-Based vs String-Based Directives

Both libraries let you define CSP using JavaScript objects, but their syntax differs slightly.

express-csp-header uses constants like csp.SELF to represent 'self':

// express-csp-header
app.use(csp({
  policies: {
    'img-src': [csp.SELF, 'data:', 'https://images.example.com']
  }
}));
// Produces: img-src 'self' data: https://images.example.com

helmet expects you to write values as strings, including quotes for special keywords:

// helmet
app.use(helmet.contentSecurityPolicy({
  directives: {
    'img-src': ["'self'", 'data:', 'https://images.example.com']
  }
}));
// Produces: img-src 'self' data: https://images.example.com

Note: In helmet, you must include the single quotes around 'self' as a string literal. This is closer to the actual HTTP header format but can be error-prone.

🔄 Maintenance and Ecosystem Fit

As of recent checks, helmet is actively maintained, widely adopted, and aligns with current OWASP security recommendations. It’s updated regularly to reflect evolving best practices (e.g., deprecating X-XSS-Protection in favor of modern CSP).

express-csp-header, while functional, shows signs of reduced maintenance. Its GitHub repository has not seen significant updates in years, and it doesn’t handle newer CSP features as gracefully. For new projects, this raises sustainability concerns.

🎯 When to Use Which?

Use helmet if:

  • You’re building a production application and want defense-in-depth.
  • You’d rather not manually configure each security header.
  • You value community support and up-to-date defaults.

Example: A SaaS dashboard serving sensitive user data.

// Recommended setup for most apps
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      'default-src': ["'self'"],
      'script-src': ["'self'", "'unsafe-inline'"], // adjust as needed
      'connect-src': ["'self'", 'https://api.yourservice.com']
    }
  }
}));

Consider express-csp-header only if:

  • You’re working in a constrained environment where you only need CSP.
  • You already manage other headers via reverse proxy (e.g., Nginx) or CDN.
  • You’ve verified compatibility with your Express version and accept the maintenance risk.

Example: A microservice that only serves JSON and relies on an edge gateway for other headers.

// Minimal CSP-only setup
app.use(csp({
  policies: {
    'default-src': [csp.NONE],
    'frame-ancestors': [csp.NONE]
  }
}));

⚠️ Critical Note on CSP Reporting

Both libraries support CSP violation reporting, but implementation differs.

helmet includes report-uri and report-to in its CSP directive options:

app.use(helmet.contentSecurityPolicy({
  directives: {
    'default-src': ["'self'"],
    'report-uri': ['/csp-violation-report-endpoint']
  }
}));

express-csp-header requires you to add the report-uri as a string in the policy array:

app.use(csp({
  policies: {
    'default-src': [csp.SELF],
    'report-uri': ['/csp-violation-report-endpoint']
  }
}));

However, note that report-uri is deprecated in favor of report-to, which requires additional header configuration. helmet makes this easier by allowing full control over all directives.

✅ Final Recommendation

For nearly all Express.js applications, helmet is the better choice. It’s secure by default, actively maintained, and reduces the chance of missing critical headers. Use express-csp-header only in niche cases where you have full control over your infrastructure and deliberately isolate CSP management — and even then, audit its compatibility carefully.

Remember: Security isn’t just about CSP. Headers like X-Content-Type-Options and Strict-Transport-Security prevent entire classes of attacks. helmet gives you those for free.

How to Choose: express-csp-header vs helmet

  • express-csp-header:

    Choose express-csp-header if you need fine-grained, programmatic control over only the Content Security Policy header and prefer to manage other security headers manually or through other means. It’s suitable for projects where CSP is the primary concern and you want to avoid pulling in a broader security toolkit. However, note that this package hasn’t seen active maintenance recently, so verify compatibility with your Express version before adoption.

  • helmet:

    Choose helmet if you want a well-maintained, batteries-included solution that automatically configures a suite of essential security headers beyond just CSP — including XSS protection, frame guards, and strict transport security. It’s the standard choice for production Express applications where defense-in-depth matters, and it allows you to customize or disable individual headers as needed.

README for express-csp-header

Content-Security-Policy middleware for Express

NPM version NPM downloads Dependency Status

Middleware wrapper for csp-header, so for more information read its documentation.

Usage

const { expressCspHeader, INLINE, NONE, SELF } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'default-src': [SELF],
        'script-src': [SELF, INLINE, 'somehost.com'],
        'style-src': [SELF, 'mystyles.net'],
        'img-src': ['data:', 'images.com'],
        'worker-src': [NONE],
        'block-all-mixed-content': true
    }
}));

// express will send header "Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' somehost.com; style-src 'self' mystyles.net; img-src data: images.com; workers-src 'none'; block-all-mixed-content; report-uri https://cspreport.com/send;'

nonce parameter

If you want to use nonce parameter you should use NONCE constant. Nonce key will be generated automatically. Also generated nonce key will be stored in req.nonce:

const { expressCspHeader, NONCE } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'script-src': [NONCE]
    }
}));
// express will send header with a random nonce key "Content-Security-Policy: script-src 'nonce-pSQ9TwXOMI+HezKshnuRaw==';"

app.use((req, res) => {
    console.log(req.nonce); // 'pSQ9TwXOMI+HezKshnuRaw=='
})

Auto tld

If you have more than one tlds you may want to have only current tld in your security policy. You can do this by replacing tld by TLD constant:

const { expressCspHeader, TLD } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'script-src': [`mystatic.${TLD}`]
    }
}));
// for myhost.com it will send: "Content-Security-Policy: script-src mystatic.com;"
// for myhost.net it will send: "Content-Security-Policy: script-src mystatic.net;"
// etc

TLD parsing options

express-csp-header uses psl package to parse tld for auto-tld feature. If you have a custom tld you can specify it as an array or a regexp.

const { expressCspHeader, TLD } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'script-src': [`mystatic.${TLD}`]
    },
    domainOptions: {
        customTlds: ['example.com']
    }
}));
// for myhost.com it will send: "Content-Security-Policy: script-src mystatic.com;"
// for myhost.example.com it will send: "Content-Security-Policy: script-src mystatic.example.com;"
// etc

Custom processing

const { expressCspHeader } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'default-src': ["#someString#"],
        'script-src': ["#someOtherString#"],
    },
    processCspString: (cspString, req, res) => {
        // here you can process final cspString
        return cspString.replaceAll('#someString#', 'https://example.com').replaceAll('#someOtherString#', 'https://example2.com');
    }
}));

CSP violation report

For more information read csp-header documentation. express-csp-header helps you manage both Content-Security-Policy and Reporting-Endpoints headers. Report-to headers is no longer recommended to use

const { expressCspHeader, INLINE, NONE, SELF } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'default-src': [SELF],
        'report-to': 'csp-default'
    },
    reportUri: 'https://cspreport.com/send',
    reportingEndpoints: [
        {'csp-default': 'https://cspreport.com/send'}
    ]  
}));

/* express will send two headers
1. Content-Security-Policy: default-src 'self'; report-to csp-default; report-uri https://cspreport.com/send;
2. Reporting-Endpoints: csp-default="https://cspreport.com/send"
*/

Presets

Read about preset in csp-header docs

Content-Security-Policy-Report-Only mode

To switch on Report-Only mode just specify reportOnly param:

const { expressCspHeader, SELF } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'script-src': [SELF]
    },
    reportOnly: true
}));
// it will send: "Content-Security-Policy-Report-Only: script-src 'self';"

report-uri parameter

const { expressCspHeader, SELF } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'script-src': [SELF]
    },
    reportUri: 'https://cspreport.com/send'
}));
// express will send header "Content-Security-Policy: script-src 'self'; report-uri https://cspreport.com/send;"

If you want to pass some params to the report uri just pass function instead of string:

const { expressCspHeader, SELF } = require('express-csp-header');

app.use(expressCspHeader({
    directives: {
        'script-src': [SELF]
    },
    reportUri: (req, res) => {
        return `https://cspreport.com/send?time=${Number(new Date())}`;
    }
}));
// express will send header "Content-Security-Policy: script-src 'self'; report-uri https://cspreport.com/send?time=1460467355592;"

Links