Skip to content

Helmet Middleware in Express

Helmet is a collection of small middleware functions that set security-related HTTP response headers, protecting against a range of well-known browser-based attacks with almost no configuration. This lesson covers what it does and how to customize it.

What Helmet Actually Does

Calling helmet() sets over a dozen HTTP headers with secure defaults in one line, headers like X-Content-Type-Options, X-Frame-Options, and Strict-Transport-Security, each addressing a specific class of attack that browsers respect.

Helmet doesn't validate input, sanitize data, or replace authentication, it hardens the transport and browser-facing layer of your app against attacks that exploit missing or misconfigured headers.

const helmet = require('helmet');
const app = express();

app.use(helmet());

This single line applies Helmet's full set of sensible default headers to every response.

Customizing Individual Protections

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", 'https://cdn.example.com'],
    },
  },
  crossOriginResourcePolicy: { policy: 'same-site' },
}));
  • Helmet applies safe defaults out of the box, customization is optional but recommended for CSP.
  • contentSecurityPolicy (CSP) restricts which sources of scripts, styles, and other resources a page may load.
  • Individual protections can be disabled by setting them to false if they conflict with your app's needs.
  • Helmet is middleware, so it must be registered before your routes to affect every response.

Helmet Headers Cheatsheet

The main protections Helmet sets by default.

Header Protects Against
X-Content-Type-Options: nosniff MIME type sniffing attacks
X-Frame-Options: SAMEORIGIN Clickjacking via iframes
Strict-Transport-Security Downgrade attacks, enforces HTTPS
Content-Security-Policy Cross-site scripting (XSS), unwanted resource loading
X-DNS-Prefetch-Control Unwanted DNS prefetching
Cross-Origin-Opener-Policy Cross-window information leaks

Content Security Policy in Practice

CSP is the most powerful, and most likely to need tuning, of Helmet's protections. If your app loads scripts, styles, or fonts from a CDN, you must explicitly allow those sources or the browser will block them.

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", 'https://fonts.googleapis.com'],
      fontSrc: ["'self'", 'https://fonts.gstatic.com'],
      imgSrc: ["'self'", 'data:', 'https:'],
    },
  },
}));

Helmet for APIs vs Server-Rendered Apps

A pure JSON API benefits mostly from the transport-level headers (HSTS, no-sniff) since it renders no HTML. A server-rendered app benefits from the full set, including CSP, since it directly controls what scripts and styles load in the browser.

Common Mistakes

  • Enabling a strict default CSP without testing it, silently breaking legitimate third-party scripts or fonts.
  • Assuming Helmet alone makes an app "secure" without addressing validation, auth, or rate limiting.
  • Registering helmet() after routes, so early responses miss the security headers.
  • Disabling Helmet protections broadly instead of configuring the specific directive that conflicts with the app.

Key Takeaways

  • Helmet sets a collection of security-related HTTP headers with one line of middleware.
  • It addresses browser-facing risks like clickjacking, MIME sniffing, and missing HTTPS enforcement.
  • Content Security Policy is the most powerful but also most likely to need explicit customization.
  • Helmet is one layer among several, not a complete security solution on its own.

Pro Tip

Add helmet() before writing any custom CSP rules, then open your browser's console while testing, blocked-resource warnings will tell you exactly which directives need loosening for your specific app.