Configure HTTP Security Headers with Helmet in Node.js
Set security HTTP headers in Express apps with Helmet — CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and CORS for OWASP-compliant web security
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Configure HTTP Security Headers with Helmet in Node.js
HTTP security headers protect your web app from clickjacking, XSS, MIME sniffing, and man-in-the-middle attacks. Helmet is an Express middleware that sets them automatically. Below: configuring Helmet with Content Security Policy (CSP), HSTS, CORS, and custom headers for OWASP compliance.
When to Use This
- Any Express.js web application or API
- Production deployments requiring OWASP security header compliance
- Apps serving HTML to browsers (CSP is critical for XSS prevention)
Prerequisites
- Node.js 18+
- Express.js project (
npm install express) - Helmet (
npm install helmet)
Solution
1. Install Dependencies
npm install express helmet cors
2. Basic Helmet Setup
const express = require("express");
const helmet = require("helmet");
const cors = require("cors");
const app = express();
// Enable Helmet with default security headers
app.use(helmet());
// Enable CORS with specific origins
app.use(cors({
origin: ["https://example.com", "https://app.example.com"],
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
maxAge: 86400, // Preflight cache: 24 hours
}));
app.get("/", (req, res) => {
res.json({ message: "Secure API" });
});
app.listen(3000, () => console.log("Server running on port 3000"));
3. Custom Content Security Policy (CSP)
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"https://cdn.jsdelivr.net",
"https://www.googletagmanager.com",
],
styleSrc: [
"'self'",
"https://fonts.googleapis.com",
"'unsafe-inline'", // Required for many CSS frameworks
],
fontSrc: [
"'self'",
"https://fonts.gstatic.com",
],
imgSrc: [
"'self'",
"data:",
"https:",
],
connectSrc: [
"'self'",
"https://api.example.com",
"https://www.google-analytics.com",
],
frameAncestors: ["'none'"], // Prevent clickjacking
baseUri: ["'self'"],
formAction: ["'self'"],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
},
}));
4. HSTS (HTTP Strict Transport Security)
app.use(helmet({
strictTransportSecurity: {
maxAge: 31536000, // 1 year in seconds
includeSubDomains: true,
preload: true,
},
}));
5. Fine-Grained Header Control
app.use(helmet({
// Prevent clickjacking
frameguard: { action: "deny" },
// Prevent MIME sniffing
noSniff: true,
// Hide X-Powered-By header
hidePoweredBy: true,
// X-Content-Type-Options
contentTypeOptions: true,
// Referrer policy
referrerPolicy: { policy: "strict-origin-when-cross-origin" },
// X-DNS-Prefetch-Control
dnsPrefetchControl: { allow: false },
// X-Download-Options (IE8)
ieNoOpen: true,
// Cross-Origin Embedder Policy
crossOriginEmbedderPolicy: { policy: "require-corp" },
// Cross-Origin Opener Policy
crossOriginOpenerPolicy: { policy: "same-origin" },
// Cross-Origin Resource Policy
crossOriginResourcePolicy: { policy: "same-origin" },
// Origin Agent Cluster
originAgentCluster: true,
}));
6. Per-Route Header Configuration
// API routes — stricter headers, no CSP
const apiHelmet = helmet({
contentSecurityPolicy: false,
frameguard: { action: "deny" },
});
app.use("/api", apiHelmet);
// Web routes — full CSP
const webHelmet = helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.jsdelivr.net"],
},
},
});
app.use("/web", webHelmet);
7. Custom Headers Middleware
// Custom security headers not covered by Helmet
app.use((req, res, next) => {
// Permissions Policy (Feature Policy successor)
res.setHeader(
"Permissions-Policy",
"camera=(), microphone=(), geolocation=(), payment=()"
);
// X-Robots-Tag (prevent indexing of API endpoints)
if (req.path.startsWith("/api")) {
res.setHeader("X-Robots-Tag", "noindex, nofollow");
}
// Cache-Control for sensitive endpoints
if (req.path.includes("/auth") || req.path.includes("/user")) {
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
}
next();
});
8. Verify Headers with Test
const request = require("supertest");
describe("Security Headers", () => {
it("should set X-Content-Type-Options", async () => {
const response = await request(app).get("/");
expect(response.headers["x-content-type-options"]).toBe("nosniff");
});
it("should set X-Frame-Options", async () => {
const response = await request(app).get("/");
expect(response.headers["x-frame-options"]).toBe("DENY");
});
it("should set Strict-Transport-Security", async () => {
const response = await request(app).get("/");
expect(response.headers["strict-transport-security"]).toContain("max-age=31536000");
});
it("should set Content-Security-Policy", async () => {
const response = await request(app).get("/");
expect(response.headers["content-security-policy"]).toContain("default-src 'self'");
});
it("should remove X-Powered-By", async () => {
const response = await request(app).get("/");
expect(response.headers["x-powered-by"]).toBeUndefined();
});
});
How It Works
- Content-Security-Policy (CSP) — the most important header. It defines which sources are allowed to load scripts, styles, images, etc. This prevents XSS by blocking inline scripts and unauthorized external resources.
- Strict-Transport-Security (HSTS) — forces the browser to use HTTPS for all future requests to this domain.
includeSubDomainsextends this to all subdomains.preloadallows submission to the HSTS preload list. - X-Frame-Options — prevents your page from being embedded in an iframe, blocking clickjacking attacks.
DENYblocks all framing;SAMEORIGINallows same-site framing. - X-Content-Type-Options: nosniff — prevents browsers from MIME-sniffing responses, which can lead to XSS by interpreting non-script files as scripts.
- Referrer-Policy — controls how much referrer information is sent with requests.
strict-origin-when-cross-originsends the full URL for same-origin requests but only the origin for cross-origin.
Variants
Nonce-Based CSP for Inline Scripts
const crypto = require("crypto");
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString("base64");
next();
});
app.use(helmet({
contentSecurityPolicy: {
useDefaults: true,
directives: {
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
},
},
}));
// Use nonce in templates
app.get("/", (req, res) => {
res.send(`
<script nonce="${res.locals.nonce}">
console.log("This inline script is allowed");
</script>
`);
});
Report-Only CSP (Testing)
// Report violations without blocking — for testing CSP before enforcement
app.use(helmet({
contentSecurityPolicy: {
reportOnly: true,
directives: {
defaultSrc: ["'self'"],
reportUri: ["/api/csp-report"],
},
},
}));
app.post("/api/csp-report", express.json({ type: "application/csp-report" }), (req, res) => {
console.log("CSP Violation:", req.body);
res.status(204).end();
});
Production vs Development
const isProduction = process.env.NODE_ENV === "production";
app.use(helmet({
contentSecurityPolicy: isProduction ? {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"],
},
} : false, // Disable CSP in dev for hot reload
hsts: isProduction ? {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
} : false, // Disable HSTS in dev (HTTP)
}));
Best Practices
-
For a deeper guide, see API Security Checklist — Authentication to Encryption.
-
Start with report-only CSP — use
reportOnly: trueto find violations before enforcing -
Use nonces for inline scripts — avoid
'unsafe-inline'in scriptSrc; use per-request nonces -
Set HSTS only over HTTPS — HSTS over HTTP is ignored by browsers
-
Test with securityheaders.com — scan your site to verify header configuration
Common Mistakes
- Using
'unsafe-inline'in scriptSrc — defeats XSS protection; use nonces instead - Forgetting to disable HSTS in development — browsers cache HSTS and won’t allow HTTP
- Not setting
frameAncestorsin CSP — X-Frame-Options is deprecated; CSPframeAncestorsis the modern equivalent - Overly permissive CORS —
origin: "*"withcredentials: trueis invalid and insecure
FAQ
Q: Does Helmet make my app secure? A: Helmet sets security headers but is not a complete security solution. You still need input validation, authentication, and secure coding practices.
Q: Should I use Helmet for API-only backends?
A: Yes. Even APIs benefit from noSniff, hidePoweredBy, and HSTS. Disable CSP for API routes since they don’t serve HTML.
Q: How do I debug CSP violations?
A: Use reportOnly: true and log violations. Browser DevTools also show CSP errors in the console.
Q: Can I use Helmet with Next.js?
A: Next.js has its own headers config. Use next.config.js headers() instead of Helmet for Next.js apps.
Is this solution production-ready?
Yes. The code examples above show tested implementations. Adapt error handling and configuration to your specific environment before deploying.
What are the performance characteristics?
Performance depends on your data volume and infrastructure. The solutions shown prioritize clarity. For high-throughput scenarios, add caching, batching, and connection pooling as needed.
How do I debug issues with this approach?
Start with the minimal example above. Add logging at each step. Test with small inputs first, then scale up. Use your language’s debugger to step through edge cases.
Related Resources
Secure JWT Refresh Token Rotation with Python
Implement secure JWT access and refresh token rotation in Python with blacklist, reuse detection, and automatic access token renewal for stateless auth
RecipePrevent SQL Injection with SQLAlchemy Parameterized Queries
Protect Python applications from SQL injection using SQLAlchemy parameterized queries, ORM models, input validation, and query inspection to ensure safe database access
RecipeStream LLM Output with Server-Sent Events (SSE)
Stream LLM responses to clients in real-time using Server-Sent Events with FastAPI, OpenAI streaming, and async generators for token-by-token output
GuideContent Security Policy: CSP Headers, Nonces, Hashes
Master Content Security Policy: CSP directives, nonces, hashes, reporting, strict-dynamic, nonce-based CSP, hash-based CSP, and production deployment patterns for web security.