본문으로 건너뛰기
Previous
Next
Helmet Complete Guide

Helmet Complete Guide

Helmet Complete Guide

이 글의 핵심

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: [],
  },
}));
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:

  1. Always use Helmet in production
  2. Configure CSP for your specific app
  3. Enable HSTS only with HTTPS
  4. Test security headers regularly
  5. Monitor CSP violations

Next Steps:

  • Configure [CORS](/en/blog/cors-complete-guide/
  • Implement [Passport Auth](/en/blog/passport-complete-guide/
  • Secure [Express API](/en/blog/express-complete-guide/

Resources:


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. Complete Helmet guide for Express.js security. Learn security headers, CSP, XSS protection, and hardening Node.js applic… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • [CORS Complete Guide](/en/blog/cors-complete-guide/
  • [Express.js Complete Guide | Fast Node.js Web Framework](/en/blog/express-complete-guide/
  • [Mongoose Complete Guide | MongoDB ODM for Node.js](/en/blog/mongoose-complete-guide/

이 글에서 다루는 키워드 (관련 검색어)

Helmet, Security, Express, Node.js, HTTP Headers, Web Security 등으로 검색하시면 이 글이 도움이 됩니다.