Helmet Complete Guide | Secure Express Apps with HTTP Headers
이 글의 핵심
Helmet helps secure Express apps by setting various HTTP headers. It's a collection of 15 smaller middleware functions that set security-related headers.
Introduction
Helmet helps secure Express apps by setting HTTP security headers. It’s not a silver bullet, but it provides an important layer of defense against common attacks.
Why Helmet?
Without Helmet, your Express app exposes information and is vulnerable:
// Without Helmet
app.get('/', (req, res) => {
res.send('Hello');
});
// Response headers:
// X-Powered-By: Express (exposes framework)
// (missing security headers)
With Helmet:
app.use(helmet());
// Response headers now include:
// Content-Security-Policy: ...
// X-DNS-Prefetch-Control: off
// X-Frame-Options: SAMEORIGIN
// Strict-Transport-Security: ...
// X-Content-Type-Options: nosniff
// (and more!)
1. Installation
npm install helmet
2. Basic Usage
const express = require('express');
const helmet = require('helmet');
const app = express();
// Use Helmet with defaults
app.use(helmet());
app.get('/', (req, res) => {
res.send('Hello, secure world!');
});
app.listen(3000);
3. What Helmet Does
Helmet is a collection of 15 smaller middleware:
app.use(helmet());
// Equivalent to:
app.use(helmet.contentSecurityPolicy());
app.use(helmet.crossOriginEmbedderPolicy());
app.use(helmet.crossOriginOpenerPolicy());
app.use(helmet.crossOriginResourcePolicy());
app.use(helmet.dnsPrefetchControl());
app.use(helmet.frameguard());
app.use(helmet.hidePoweredBy());
app.use(helmet.hsts());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.originAgentCluster());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());
4. Content Security Policy (CSP)
CSP prevents XSS attacks by controlling which resources can be loaded:
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
imgSrc: ["'self'", "data:", "https:"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
connectSrc: ["'self'", "https://api.example.com"],
frameSrc: ["'none'"],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
}));
Strict CSP (Recommended)
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"],
imgSrc: ["'self'"],
fontSrc: ["'self'"],
connectSrc: ["'self'"],
frameSrc: ["'none'"],
objectSrc: ["'none'"],
},
}));
CSP for React/Vue Apps
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"], // React inline scripts
styleSrc: ["'self'", "'unsafe-inline'"], // CSS-in-JS
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", process.env.API_URL],
},
}));
5. HTTP Strict Transport Security (HSTS)
Forces HTTPS connections:
app.use(helmet.hsts({
maxAge: 31536000, // 1 year in seconds
includeSubDomains: true,
preload: true,
}));
// Only enable in production with HTTPS
if (process.env.NODE_ENV === 'production') {
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
}));
}
6. X-Frame-Options
Prevents clickjacking:
// Deny all framing
app.use(helmet.frameguard({ action: 'deny' }));
// Allow same origin
app.use(helmet.frameguard({ action: 'sameorigin' }));
// Allow specific domain
app.use(helmet.frameguard({
action: 'allow-from',
domain: 'https://example.com'
}));
7. X-Content-Type-Options
Prevents MIME sniffing:
app.use(helmet.noSniff());
// Sets header:
// X-Content-Type-Options: nosniff
8. Referrer Policy
Controls Referer header:
app.use(helmet.referrerPolicy({
policy: 'strict-origin-when-cross-origin'
}));
// Options:
// - no-referrer
// - no-referrer-when-downgrade
// - origin
// - origin-when-cross-origin
// - same-origin
// - strict-origin
// - strict-origin-when-cross-origin (recommended)
// - unsafe-url
9. Hide Powered By
Remove X-Powered-By header:
app.use(helmet.hidePoweredBy());
// Or manually:
app.disable('x-powered-by');
// Or fake it:
app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }));
10. Custom Configuration
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
},
frameguard: {
action: 'deny',
},
referrerPolicy: {
policy: 'strict-origin-when-cross-origin',
},
}));
11. Disable Specific Middleware
app.use(helmet({
contentSecurityPolicy: false, // Disable CSP
frameguard: false, // Disable X-Frame-Options
}));
12. Production Configuration
const express = require('express');
const helmet = require('helmet');
const app = express();
// Production-ready Helmet config
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", process.env.CDN_URL],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", process.env.API_URL],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
frameguard: {
action: 'deny',
},
referrerPolicy: {
policy: 'strict-origin-when-cross-origin',
},
}));
// Additional security
app.disable('x-powered-by');
// Trust proxy (if behind reverse proxy)
app.set('trust proxy', 1);
app.listen(process.env.PORT || 3000);
13. With CORS
const helmet = require('helmet');
const cors = require('cors');
// Apply Helmet first
app.use(helmet());
// Then CORS
app.use(cors({
origin: process.env.FRONTEND_URL,
credentials: true,
}));
14. Testing Security Headers
# Check headers with curl
curl -I https://example.com
# Look for:
# Content-Security-Policy: ...
# Strict-Transport-Security: max-age=31536000; includeSubDomains
# X-Frame-Options: DENY
# X-Content-Type-Options: nosniff
# Referrer-Policy: strict-origin-when-cross-origin
Security Scanners
# Mozilla Observatory
# https://observatory.mozilla.org/
# Security Headers
# https://securityheaders.com/
# SSL Labs
# https://www.ssllabs.com/ssltest/
15. Common Issues
Issue 1: CSP Blocking Inline Scripts
// Problem: React/Vue inline scripts blocked
<script>window.__INITIAL_STATE__ = {...}</script>
// Solution 1: Use nonce
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64');
next();
});
app.use(helmet.contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
},
}));
// In template:
<script nonce="<%= nonce %>">...</script>
// Solution 2: Allow unsafe-inline (less secure)
app.use(helmet.contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", "'unsafe-inline'"],
},
}));
Issue 2: HSTS Breaking Local Development
// Only enable HSTS in production
if (process.env.NODE_ENV === 'production') {
app.use(helmet.hsts({
maxAge: 31536000,
}));
}
Issue 3: CSP Blocking CDN Resources
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"https://cdn.jsdelivr.net",
"https://cdnjs.cloudflare.com",
],
styleSrc: [
"'self'",
"https://fonts.googleapis.com",
],
},
}));
16. Best Practices
1. Use Helmet in All Environments
// Development
app.use(helmet({
contentSecurityPolicy: false, // Easier debugging
}));
// Production
app.use(helmet()); // Full security
2. Test Thoroughly
// Test CSP in report-only mode first
app.use(helmet.contentSecurityPolicy({
directives: { /* ... */ },
reportOnly: true, // Only report violations, don't block
}));
3. Monitor CSP Violations
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
reportUri: '/api/csp-violation-report',
},
}));
app.post('/api/csp-violation-report', (req, res) => {
console.log('CSP Violation:', req.body);
// Log to monitoring service
res.status(204).end();
});
Summary
Helmet provides essential security headers:
- 15 security middlewares in one package
- CSP prevents XSS attacks
- HSTS enforces HTTPS
- Frameguard prevents clickjacking
- Production-ready with minimal config
Key Takeaways:
- Always use Helmet in production
- Configure CSP for your specific app
- Enable HSTS only with HTTPS
- Test security headers regularly
- Monitor CSP violations
Next Steps:
- Configure CORS
- Implement Passport Auth
- Secure Express API
Resources: