Hardening an Express App with KrakenJS's Helmet Middleware
A fresh Express server ships with almost no security headers. KrakenJS's Helmet middleware fixes that with one line — here's what it sets, how to tune the CSP per route, and where it falls short.
17 Mar 2026, 10:31 UTC

The problem: your Express app ships with unsafe defaults
A fresh Express server sends almost no security-related HTTP headers. That leaves the door open to a set of well-known attacks that require no code vulnerability at all:
- Click-jacking: an attacker embeds your page in an invisible iframe and tricks users into clicking things they never intended to.
- MIME sniffing: a browser ignores the declared
Content-Typeand executes a file you meant to serve as plain data. - Referrer leakage: full URLs, including query strings, are passed to third parties through the
Refererheader.
The fix is not glamorous, but it is cheap: KrakenJS's helmet middleware sets a hardened header baseline with a single line of code. This post shows what it does, how to configure it, and where its limits are.
What Helmet actually sets
Calling app.use(helmet()) applies a collection of small middleware functions, each responsible for one header. The defaults include:
X-Frame-Options: SAMEORIGIN— blocks other origins from framing your pages.X-Content-Type-Options: nosniff— disables MIME sniffing.Referrer-Policy— controls how much referrer information is shared.Content-Security-Policy(CSP) — a conservative policy that, among other things, blocks inline scripts unless explicitly allowed.- Additional headers such as
X-DNS-Prefetch-ControlandStrict-Transport-Security.
One caveat: the exact default header set changes between Helmet major versions. Check the changelog for the version you install rather than assuming a specific list.
Installing and applying it
From your project root (no special permissions needed):
npm install helmetThen register it before your routes in your entry file (for example app.js):
const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet());
app.get('/', (req, res) => {
res.send('Hello, secure world!');
});
app.listen(3000);Order matters: Helmet must run before any route handler that sends a response, or those responses will miss the headers.
Worked example: a per-route Content-Security-Policy
The default CSP blocks inline scripts and styles, which is exactly what you want — until one page legitimately needs a CDN-hosted library. You can override the policy for a single route while keeping the strict default everywhere else:
app.get('/dashboard',
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'https://cdn.jsdelivr.net'],
styleSrc: ["'self'", 'https://cdn.jsdelivr.net'],
imgSrc: ["'self'", 'data:']
}
}),
(req, res) => {
res.send('Dashboard with CDN assets allowed');
}
);Why this helps: if an attacker manages to inject a <script> tag into the dashboard page, the browser refuses to execute it because the source is not in scriptSrc. The CSP turns a successful injection into a broken page instead of a compromised session.
Verifying the headers
Start the server and inspect the response headers from your terminal:
curl -I http://localhost:3000You should see X-Content-Type-Options: nosniff, X-Frame-Options, and a Content-Security-Policy line in the output. Repeat against /dashboard to confirm the custom CSP is applied there and only there. Browser dev tools (Network tab → select a request → Headers) give the same view for rendered pages.
Trade-offs and limitations
- CSP breakage: the default policy blocks inline scripts and styles. Legacy front-ends that rely on them will break until you either refactor or relax the policy. Test behind a staging environment before rolling out.
- Headers are not a firewall: Helmet mitigates specific browser-side attack classes. It does nothing for SQL injection, broken authentication, or missing output encoding — you still need code-level security practices.
- Deprecated headers: some headers Helmet historically set (such as
X-XSS-Protection) are deprecated in modern browsers. Audit the header set for your audience rather than assuming every default is still best practice.
Actionable next steps
Add app.use(helmet()) to your Express app today, verify the headers with curl -I, then scan your deployed site with a header analysis tool such as Mozilla Observatory. Where the defaults break legitimate functionality, tune the specific directive instead of disabling the middleware. It is one of the lowest-cost, highest-impact hardening steps available to an Express application.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.