Front-end Engineering Lab

Security Headers

Configure HTTP security headers to protect against common web vulnerabilities.

HSTS & Security Headers

The Risk

Without security headers, your application is vulnerable to:

  • Man-in-the-middle attacks (no HTTPS enforcement)
  • Clickjacking (iframe embedding)
  • MIME sniffing (content type confusion)
  • XSS (inline scripts allowed)

Essential Security Headers

/**
 * Security headers configuration
 */
interface SecurityHeaders {
  'Strict-Transport-Security': string;
  'X-Content-Type-Options': string;
  'X-Frame-Options': string;
  'X-XSS-Protection': string;
  'Referrer-Policy': string;
  'Permissions-Policy': string;
  'Content-Security-Policy': string;
}

const SECURITY_HEADERS: SecurityHeaders = {
  // Force HTTPS for 2 years
  'Strict-Transport-Security': 'max-age=63072000; includeSubDomains; preload',
  
  // Prevent MIME sniffing
  'X-Content-Type-Options': 'nosniff',
  
  // Prevent clickjacking
  'X-Frame-Options': 'DENY',
  
  // Enable XSS filter (legacy browsers)
  'X-XSS-Protection': '1; mode=block',
  
  // Control referrer information
  'Referrer-Policy': 'strict-origin-when-cross-origin',
  
  // Restrict browser features
  'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
  
  // Content Security Policy (see CSP pattern)
  'Content-Security-Policy': "default-src 'self'; script-src 'self'",
};

Server Configuration Examples

Express.js (Node.js)

import express from 'express';
import helmet from 'helmet';

const app = express();

// Use Helmet for security headers
app.use(helmet({
  strictTransportSecurity: {
    maxAge: 63072000,
    includeSubDomains: true,
    preload: true,
  },
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", 'data:', 'https:'],
    },
  },
  xFrameOptions: { action: 'deny' },
}));

// Custom headers
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  next();
});

Next.js Configuration

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=63072000; includeSubDomains; preload',
          },
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff',
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin',
          },
          {
            key: 'Permissions-Policy',
            value: 'geolocation=(), microphone=(), camera=()',
          },
        ],
      },
    ];
  },
};

Nginx Configuration

# nginx.conf
server {
    listen 443 ssl http2;
    server_name example.com;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    
    # Prevent clickjacking
    add_header X-Frame-Options "DENY" always;
    
    # Prevent MIME sniffing
    add_header X-Content-Type-Options "nosniff" always;
    
    # XSS Protection
    add_header X-XSS-Protection "1; mode=block" always;
    
    # Referrer Policy
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # CSP
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;
}

CloudFront (AWS CDN)

{
  "Comment": "Security headers for CloudFront",
  "SecurityHeadersPolicy": {
    "StrictTransportSecurity": {
      "Override": true,
      "AccessControlMaxAgeSec": 63072000,
      "IncludeSubdomains": true,
      "Preload": true
    },
    "XContentTypeOptions": {
      "Override": true
    },
    "XFrameOptions": {
      "Override": true,
      "FrameOption": "DENY"
    },
    "XSSProtection": {
      "Override": true,
      "Protection": true,
      "ModeBlock": true
    },
    "ReferrerPolicy": {
      "Override": true,
      "ReferrerPolicy": "strict-origin-when-cross-origin"
    }
  }
}

Header Descriptions

Strict-Transport-Security (HSTS)

Forces HTTPS connections for specified duration.

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  • max-age: 2 years in seconds
  • includeSubDomains: Apply to all subdomains
  • preload: Include in browser preload list

X-Frame-Options

Prevents clickjacking by controlling iframe embedding.

X-Frame-Options: DENY

Options:

  • DENY: No framing allowed
  • SAMEORIGIN: Only same origin
  • ALLOW-FROM uri: Specific origin (deprecated)

X-Content-Type-Options

Prevents MIME type sniffing.

X-Content-Type-Options: nosniff

Referrer-Policy

Controls how much referrer information is sent.

Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policy

Controls browser features.

Permissions-Policy: geolocation=(), microphone=(), camera=()

Validation Tool

/**
 * Check if headers are properly configured
 */
async function validateSecurityHeaders(url: string): Promise<void> {
  const response = await fetch(url);
  const headers = response.headers;

  const requiredHeaders = [
    'strict-transport-security',
    'x-content-type-options',
    'x-frame-options',
  ];

  const missing: string[] = [];

  requiredHeaders.forEach((header) => {
    if (!headers.has(header)) {
      missing.push(header);
    }
  });

  if (missing.length > 0) {
    console.warn('Missing security headers:', missing);
  } else {
    console.log('✓ All required security headers present');
  }
}

OWASP Protection

Security headers protect against:

  • A05:2021 – Security Misconfiguration
  • A03:2021 – Injection
  • Clickjacking
  • MIME sniffing attacks

Best Practices

  1. HSTS: Always use with max-age 2+ years
  2. CSP: Start strict, relax as needed
  3. Test thoroughly: Headers can break functionality
  4. Use helmet.js: For Node.js apps
  5. Check with tools: SecurityHeaders.com
  6. Preload HSTS: Submit to hstspreload.org
  7. Monitor: Log CSP violations

Testing

# Check headers with curl
curl -I https://example.com

# Test with Security Headers scanner
https://securityheaders.com

# Test CSP with browser console
# Check for CSP violations in console

Security headers are free protection with zero performance cost. Configure them immediately.

On this page