What Helmet Sets by Default

This reference is part of the Node.js Express Helmet configuration reference and enumerates the exact response headers a bare app.use(helmet()) produces in Express. Each entry gives the literal header value Helmet emits, the protection it provides, and the option key that turns it off. The values below are Helmet v7 and v8 defaults; they are stable across those major versions unless you override them.

A single helmet() call registers fourteen sub-middlewares. Thirteen add a header; one removes one. Nothing here depends on your routes — Helmet writes these on every response the middleware sees.

Configuration Syntax & Exact Values

The minimal call and the complete set of headers it emits:

const express = require("express");
const helmet = require("helmet");

const app = express();
app.use(helmet());

That produces exactly these response headers on every route registered after it:

Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Origin-Agent-Cluster: ?1
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-XSS-Protection: 0

Helmet also removes X-Powered-By, which Express sets to Express by default and which leaks your stack to attackers. There is no header to show for it — its absence is the effect. Note two headers that Helmet does not set by default: Cross-Origin-Embedder-Policy (off, because require-corp breaks pages that load un-CORP cross-origin subresources) and any Permissions-Policy.

One helmet() call fans out to thirteen headers and one removal A single helmet middleware call branches into the individual header-setting sub-middlewares plus the X-Powered-By removal. app.use( helmet()) 13 headers set + 1 header removed, every response Content-Security-Policy Cross-Origin-Opener-Policy Cross-Origin-Resource-Policy Origin-Agent-Cluster Referrer-Policy Strict-Transport-Security X-Content-Type-Options X-DNS-Prefetch-Control X-Download-Options X-Frame-Options X-Permitted-Cross-Domain-Policies X-XSS-Protection X-Powered-By (removed) Cross-Origin-Embedder-Policy and Permissions-Policy are NOT set by default
A bare helmet() call registers thirteen header-setting middlewares plus the X-Powered-By removal.

The annotated table below is the reference proper. Each row lists the emitted value, the threat it addresses, and the exact key to set to false inside the helmet({ ... }) options object to switch that piece off.

Header (default value) Protects against Disable with
Content-Security-Policy: default-src 'self'; … Cross-site scripting, mixed content, clickjacking (via frame-ancestors) contentSecurityPolicy: false
Cross-Origin-Opener-Policy: same-origin Cross-window attacks; enables cross-origin isolation crossOriginOpenerPolicy: false
Cross-Origin-Resource-Policy: same-origin Cross-origin embedding of your resources (Spectre-class reads) crossOriginResourcePolicy: false
Origin-Agent-Cluster: ?1 Shared memory / same-site origin bleed; requests origin-keyed agent cluster originAgentCluster: false
Referrer-Policy: no-referrer Leaking full URLs (paths, query) to other origins referrerPolicy: false
Strict-Transport-Security: max-age=31536000; includeSubDomains Protocol downgrade / SSL-strip on HTTP strictTransportSecurity: false
X-Content-Type-Options: nosniff MIME sniffing that turns uploads into scripts xContentTypeOptions: false
X-DNS-Prefetch-Control: off DNS-prefetch privacy leaks xDnsPrefetchControl: false
X-Download-Options: noopen Legacy IE opening downloads in site context xDownloadOptions: false
X-Frame-Options: SAMEORIGIN Clickjacking in legacy browsers without CSP xFrameOptions: false
X-Permitted-Cross-Domain-Policies: none Adobe Flash/PDF cross-domain policy abuse xPermittedCrossDomainPolicies: false
X-XSS-Protection: 0 Disables buggy legacy XSS auditors (intentionally 0) xXssProtection: false
X-Powered-By removed Stack fingerprinting xPoweredBy: false (keeps the header)

Two rows carry a trap. X-XSS-Protection: 0 deliberately turns the old browser XSS auditor off — those auditors introduced their own vulnerabilities and are gone from modern browsers, so Helmet neutralises them rather than enabling them. And xPoweredBy: false means “do not run the removal middleware”, which leaves Express’s X-Powered-By: Express in place — the inverse of what the name suggests.

Server-Side Configuration

Helmet is Express middleware; “server-side configuration” here means the JavaScript that mounts and tunes it. Every block below is runnable.

Enable all defaults

Mount Helmet before your routes so its headers apply to their responses. Middleware registered after a route handler that already sent a response never runs.

const express = require("express");
const helmet = require("helmet");

const app = express();
app.use(helmet()); // all defaults, must precede route handlers

app.get("/", (req, res) => res.send("ok"));
app.listen(3000);

Tune the two values you most often change

The default CSP allows 'unsafe-inline' styles and blocks all inline and remote scripts except same-origin, which breaks most real apps. The HSTS max-age of one year is correct for production but must not be sent from localhost or staging on plain HTTP. Override just those, keeping every other default:

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        scriptSrc: ["'self'", "https://cdn.example.com"],
      },
    },
    strictTransportSecurity: {
      maxAge: 63072000, // 2 years
      includeSubDomains: true,
      preload: true,
    },
  })
);

Passing a directives object with useDefaults left at its default of true merges your entries onto Helmet’s baseline CSP — here only scriptSrc is replaced and every other default directive is retained. For a full nonce-based policy, see configuring CSP with Helmet and per-request nonces; for stripping directives, see customizing and disabling Helmet CSP directives.

Disable specific middlewares

Set the option key to false. This disables the Content-Security-Policy so you can add it at a proxy, and turns off Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy because you serve embeddable widgets:

app.use(
  helmet({
    contentSecurityPolicy: false,
    crossOriginOpenerPolicy: false,
    crossOriginResourcePolicy: false,
  })
);

Only remove X-Powered-By

If you want none of Helmet’s headers except the fingerprint fix, call the single sub-middleware instead of the bundle:

const helmet = require("helmet");
app.use(helmet.xPoweredBy()); // removes X-Powered-By, adds nothing

Which defaults are safe to ship untouched versus which demand a decision depends on what your app serves. The tree below is the triage.

Keep, tune, or disable each Helmet default A decision tree sorting Helmet's default headers into ship-as-is, tune-before-production, and conditionally-disable groups. Default header from helmet() Ship as-is no config needed Tune first value likely breaks app Disable if it applies only when hosting model demands X-Content-Type-Options Referrer-Policy Origin-Agent-Cluster X-Frame-Options X-Powered-By removal Content-Security-Policy Strict-Transport-Security Cross-Origin-Resource-Policy Cross-Origin-Opener-Policy Rule of thumb Never disable a header to fix a bug you have not diagnosed — tune CSP and CORP; leave the one-line hardening headers alone.
Most defaults ship untouched; CSP and HSTS need tuning; the cross-origin pair depends on whether you serve embeddable resources.

Diagnostic & Verification Steps

Confirm the full default set with a single curl -I. The -I sends a HEAD request and prints only response headers.

curl -sI http://localhost:3000/

Expected output includes every Helmet header and, crucially, no X-Powered-By line:

HTTP/1.1 200 OK
Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Origin-Agent-Cluster: ?1
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-XSS-Protection: 0

Assert that X-Powered-By is gone — the grep must print nothing and exit non-zero:

# Should return no output; a match means the removal middleware did not run
curl -sI http://localhost:3000/ | grep -i '^x-powered-by:'

Count how many of Helmet’s headers are present to catch a partial or duplicated set:

curl -sI http://localhost:3000/ | grep -ciE '^(content-security-policy|cross-origin-opener-policy|cross-origin-resource-policy|origin-agent-cluster|referrer-policy|strict-transport-security|x-content-type-options|x-dns-prefetch-control|x-download-options|x-frame-options|x-permitted-cross-domain-policies|x-xss-protection):'

Expected output: 12. A lower number means Helmet ran after a route already responded, or a middleware was disabled. A higher number means a proxy in front of Express duplicated a header. In Chrome DevTools, open the Network panel, select the document request, and read the Response Headers section; the same twelve names appear there, and the General section will not list X-Powered-By.

The response is assembled in a fixed order: Express would normally stamp X-Powered-By, Helmet’s stack removes it and layers the security headers, then your handler sends the body. The sequence below shows where each mutation happens.

Where Helmet mutates the response in the middleware chain A request enters Express, passes through the Helmet middleware which removes X-Powered-By and adds security headers, then reaches the route handler that sends the body. Browser GET / Express sets X-Powered-By helmet() - removes X-Powered-By + 12 security headers route handler res.send(body) helmet() must be registered before the handler headers set after res.send() are silently dropped
Helmet mutates headers between Express's defaults and your handler; register it before any route or its headers never reach the client.

Edge Cases, Security Implications & Safe Rollback

Safe rollback. Helmet is code-level and non-destructive, but HSTS persists in clients. To back out:

  1. Remove or narrow the offending option (for example strictTransportSecurity: false) and redeploy.
  2. If a bad HSTS max-age reached clients, send Strict-Transport-Security: max-age=0 from HTTPS so browsers expire the pin on their next visit; only then remove the header entirely.
  3. Re-run the curl -sI checks above and confirm the header set matches your intent.

Conclusion

Add app.use(helmet()) on staging first and read the twelve headers with curl -sI; tune the CSP for your scripts and set HSTS to a short max-age such as 300 while you verify nothing breaks over HTTPS. Once staging is clean, raise max-age to a year with includeSubDomains and ship to production, leaving the one-line hardening headers at their defaults.

Frequently Asked Questions

Does Helmet set Cross-Origin-Embedder-Policy by default? No. Cross-Origin-Embedder-Policy is off by default because its require-corp value blocks any cross-origin subresource that does not send a matching CORP or CORS header, which breaks most existing pages. Enable it deliberately with crossOriginEmbedderPolicy: true only when you need full cross-origin isolation for SharedArrayBuffer or precise timers.

Why is X-XSS-Protection set to 0 instead of 1? The legacy browser XSS auditor introduced its own vulnerabilities and has been removed from Chromium and Firefox. Sending X-XSS-Protection: 0 explicitly disables any remaining auditor so it cannot be abused. Modern XSS defence comes from Content-Security-Policy, not this header.

How do I keep the X-Powered-By header if I want it? Set xPoweredBy: false in the Helmet options. That disables the removal middleware, so Express’s X-Powered-By: Express stays. Note the inverted meaning: false here means “keep the header”, the opposite of every other option key.

Does the default CSP break my inline scripts and third-party assets? Yes for scripts. The default script-src 'self' blocks inline <script> and any remote script, though style-src 'self' https: 'unsafe-inline' still permits inline styles. Add your script origins or a per-request nonce to scriptSrc, keeping the other default directives via useDefaults: true.

Is the HSTS max-age of 31536000 safe to ship immediately? Only from HTTPS production. One year of includeSubDomains means every browser that receives it refuses HTTP to your host and subdomains for a year. Test with a short max-age first, confirm all subdomains serve valid HTTPS, then raise it — and only add preload when you are certain.