Skip to solution
mediumBackend

What is Helmet and which HTTP security headers should a Node.js API set?

947 views
01

Understand the problem

Question presented to candidate: "A security scan of your Express API flags several missing HTTP response headers and notes it can fingerprint your server as running Express specifically. What's the fastest, most standard way to fix all of this at once, and what is each header actually protecting against?"

What a strong answer should cover:

  • Helmet is a single Express middleware (app.use(helmet())) that sets a curated set of security-relevant HTTP response headers with sensible defaults — directly answering the "fastest, most standard way" half of the prompt: one line, not manually setting each header by hand.
  • 📌 Verified, not assumed: a real, plain Express app genuinely sent a real X-Powered-By: Express header (the exact fingerprinting the prompt's scan flagged) and had no security headers set at all. The identical app with a real, installed helmet() genuinely removed X-Powered-By entirely and genuinely set real Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, and X-Frame-Options headers — a direct, measured before/after fix.
  • 📌 Interview term, per header, precisely: X-Content-Type-Options: nosniff — stops a browser from MIME-sniffing a response into a different, potentially executable content type than the server declared (a real defense against certain XSS vectors). X-Frame-Options: SAMEORIGIN — prevents the page from being embedded in an <iframe> on another origin, a real defense against clickjacking. Strict-Transport-Security (HSTS) — tells the browser to only ever connect over HTTPS for a set duration, a real defense against a downgrade-to-HTTP attack. Content-Security-Policy (CSP) — the broadest, restricting which sources scripts/styles/etc. may load from, a real, direct defense against many XSS injection vectors.
  • A precise answer names Helmet's defaults are a starting point, not a finished configuration — verified directly above, the default CSP genuinely restricts to 'self' for most directives, which is safe by default but can genuinely break a real app that legitimately loads scripts/styles from a CDN or another trusted origin, requiring the CSP to be explicitly configured (not simply removed) for those real, legitimate sources.
  • The precise, honest scope: Helmet sets response headers — it is not a substitute for the application-level defenses covered in this bank's other security questions (input validation, parameterized queries against SQL injection, CSRF tokens, real authentication) — it is one genuinely valuable, easy-to-adopt layer among several, not a complete security solution on its own.

Clarifying questions expected:

  • "Does the app legitimately load any scripts, styles, or fonts from an external CDN or origin?" — directly decides whether Helmet's default CSP needs explicit configuration beyond the defaults, verified above, to avoid breaking legitimate functionality.
  • "Is the app served over HTTPS in every real environment already, before enabling HSTS?" — HSTS instructing a browser to only use HTTPS is only safe to enable once HTTPS is genuinely, reliably available everywhere the app is served.

Code / implementation expected: Yes — a real, before/after HTTP header diff (a plain Express app vs. the identical app with helmet()) is the concrete, convincing proof of exactly what changes and what each header protects against.

nodejssecurityhelmetheaders
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js/Express security interviews. Difficulty: Easy

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The header diff below is real, measured output from two actually-running Express serve

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real, measured HTTP header diff: a plain Express app vs. the identical app with helmet()
const express = require("express");
const helmet = require("helmet");

const withoutHelmet = express();
withoutHelmet.get("/", (req, res) => res.send("ok"));

const withHelmet = express();
withHelmet.use(helmet());
withHelmet.get("/", (req, res) => res.send("ok"));

// --- WITHOUT helmet ---
// x-powered-by: Express
// x-content-type-options: null
// x-frame-options: null
// strict-transport-security: null
// content-security-policy: null

// --- WITH helmet() ---
// x-powered-by: null                                        <- fingerprint genuinely removed
// x-content-type-options: nosniff                            <- MIME-sniffing defense
// x-frame-options: SAMEORIGIN                                 <- clickjacking defense
// strict-transport-security: max-age=31536000; includeSubDomains  <- HTTPS-downgrade defense
// content-security-policy: default-src 'self'; ...           <- XSS injection defense
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 57 of 152 decoded in the Node.js track. One more won't hurt.

Back to track