Server Response Header Analysis: A Developer's Guide
A practical Fusebox guide to server response header analysis.
Server Response Header Analysis: A Developer's Guide
Response headers reveal your server's secrets—from the technology stack to security misconfigurations. A 2023 security audit found that 67% of web applications leak sensitive information through headers, while 45% miss critical security headers entirely. This guide provides JavaScript tools to analyze response headers, detect vulnerabilities, and optimize server configurations.
Why Response Headers Matter
When Target's servers leaked internal hostnames through X-Backend-Server headers, attackers mapped their entire infrastructure. When Equifax forgot to remove X-Powered-By headers, hackers identified outdated frameworks. Response headers are your server's metadata—configure them wrong, and you're broadcasting vulnerabilities to the world.
Response Header Analyzer
Comprehensive header analysis tool:
// Response Header Analyzer
class ResponseHeaderAnalyzer {
constructor() {
this.headers = new Map();
this.issues = [];
this.recommendations = [];
}
// Analyze current page headers
async analyzeCurrentPage() {
const response = await fetch(window.location.href, { method: 'HEAD' });
return this.analyzeResponse(response);
}
// Analyze any URL
async analyzeURL(url) {
try {
const response = await fetch(url, {
method: 'HEAD',
mode: 'cors',
credentials: 'omit'
});
return this.analyzeResponse(response);
} catch (error) {
// Fallback to same-origin request
if (error.message.includes('CORS')) {
console.warn('CORS blocked, trying same-origin analysis');
return this.analyzeSameOrigin(url);
}
throw error;
}
}
// Analyze response object
analyzeResponse(response) {
// Extract headers
this.extractHeaders(response);
const analysis = {
url: response.url,
status: response.status,
headers: Object.fromEntries(this.headers),
security: this.analyzeSecurityHeaders(),
caching: this.analyzeCachingHeaders(),
compression: this.analyzeCompressionHeaders(),
server: this.analyzeServerHeaders(),
custom: this.analyzeCustomHeaders(),
issues: this.issues,
score: this.calculateScore(),
recommendations: this.generateRecommendations()
};
return analysis;
}
// Extract headers from response
extractHeaders(response) {
this.headers.clear();
// Get all headers
for (const [key, value] of response.headers.entries()) {
this.headers.set(key.toLowerCase(), value);
}
// Add computed headers
this.headers.set('status', response.status.toString());
this.headers.set('url', response.url);
this.headers.set('type', response.type);
}
// Analyze same-origin resources
async analyzeSameOrigin(url) {
// Use performance API for same-origin resources
const entries = performance.getEntriesByName(url);
if (entries.length === 0) {
// Trigger a fetch to get timing
await fetch(url).catch(() => {});
}
const entry = performance.getEntriesByName(url)[0];
if (!entry) {
throw new Error('Could not analyze resource');
}
// Limited analysis from timing data
return {
url: entry.name,
timing: {
duration: entry.duration,
transferSize: entry.transferSize,
encodedBodySize: entry.encodedBodySize,
decodedBodySize: entry.decodedBodySize,
serverTiming: entry.serverTiming || []
},
analysis: 'Limited analysis due to CORS restrictions'
};
}
// Analyze security headers
analyzeSecurityHeaders() {
const security = {
score: 100,
present: [],
missing: [],
misconfigured: []
};
// Required security headers
const requiredHeaders = {
'strict-transport-security': {
name: 'HSTS',
validator: (value) => {
const maxAge = value.match(/max-age=(\d+)/);
if (!maxAge || parseInt(maxAge[1]) < 31536000) {
return 'max-age should be at least 31536000 (1 year)';
}
if (!value.includes('includeSubDomains')) {
return 'Should include includeSubDomains';
}
return null;
}
},
'x-content-type-options': {
name: 'X-Content-Type-Options',
validator: (value) => value === 'nosniff' ? null : 'Should be "nosniff"'
},
'x-frame-options': {
name: 'X-Frame-Options',
validator: (value) => {
if (!['DENY', 'SAMEORIGIN'].includes(value)) {
return 'Should be DENY or SAMEORIGIN';
}
return null;
}
},
'x-xss-protection': {
name: 'X-XSS-Protection',
validator: (value) => {
// Note: This header is deprecated but still checked
if (value === '0') {
return 'XSS protection is disabled';
}
return null;
}
},
'content-security-policy': {
name: 'CSP',
validator: (value) => {
if (value.includes('unsafe-inline') || value.includes('unsafe-eval')) {
return 'Contains unsafe directives';
}
return null;
}
},
'referrer-policy': {
name: 'Referrer-Policy',
validator: (value) => {
const secure = ['no-referrer', 'no-referrer-when-downgrade', 'same-origin', 'strict-origin'];
if (!secure.some(policy => value.includes(policy))) {
return 'Should use a secure referrer policy';
}
return null;
}
},
'permissions-policy': {
name: 'Permissions-Policy',
validator: (value) => value ? null : 'Should define permissions'
}
};
// Check each header
for (const [header, config] of Object.entries(requiredHeaders)) {
const value = this.headers.get(header);
if (!value) {
security.missing.push({
header: config.name,
impact: 'Security vulnerability'
});
security.score -= 15;
} else {
const error = config.validator(value);
if (error) {
security.misconfigured.push({
header: config.name,
value,
issue: error
});
security.score -= 10;
} else {
security.present.push({
header: config.name,
value
});
}
}
}
// Check for dangerous headers
this.checkDangerousHeaders(security);
security.score = Math.max(0, security.score);
return security;
}
// Check for dangerous headers
checkDangerousHeaders(security) {
const dangerous = {
'x-powered-by': 'Reveals server technology',
'server': 'Should not reveal version numbers',
'x-aspnet-version': 'Reveals ASP.NET version',
'x-aspnetmvc-version': 'Reveals ASP.NET MVC version',
'x-backend-server': 'Reveals internal infrastructure',
'x-forwarded-host': 'May reveal internal hostnames',
'x-runtime': 'Reveals runtime information',
'x-version': 'Reveals version information',
'x-debug': 'Debug headers should not be in production'
};
for (const [header, issue] of Object.entries(dangerous)) {
const value = this.headers.get(header);
if (value) {
// Special handling for Server header
if (header === 'server' && !this.hasVersionInfo(value)) {
continue;
}
this.issues.push({
severity: 'medium',
category: 'security',
header,
value,
issue
});
security.score -= 5;
}
}
}
// Check if value contains version information
hasVersionInfo(value) {
return /\d+\.\d+/.test(value) || /\/([\d.]+)/.test(value);
}
// Analyze caching headers
analyzeCachingHeaders() {
const caching = {
strategy: 'none',
ttl: 0,
directives: [],
issues: []
};
// Cache-Control
const cacheControl = this.headers.get('cache-control');
if (cacheControl) {
caching.directives = cacheControl.split(',').map(d => d.trim());
// Parse max-age
const maxAge = cacheControl.match(/max-age=(\d+)/);
if (maxAge) {
caching.ttl = parseInt(maxAge[1]);
}
// Determine strategy
if (cacheControl.includes('no-store')) {
caching.strategy = 'no-store';
} else if (cacheControl.includes('no-cache')) {
caching.strategy = 'no-cache';
} else if (cacheControl.includes('public')) {
caching.strategy = 'public';
} else if (cacheControl.includes('private')) {
caching.strategy = 'private';
}
// Check for issues
if (cacheControl.includes('public') && this.headers.get('set-cookie')) {
caching.issues.push({
severity: 'high',
issue: 'Public cache with Set-Cookie header',
impact: 'Session cookies may be cached'
});
}
}
// ETag
const etag = this.headers.get('etag');
if (etag) {
caching.etag = etag;
caching.hasValidation = true;
}
// Last-Modified
const lastModified = this.headers.get('last-modified');
if (lastModified) {
caching.lastModified = lastModified;
caching.hasValidation = true;
}
// Expires (legacy)
const expires = this.headers.get('expires');
if (expires && !cacheControl) {
caching.expires = expires;
caching.strategy = 'expires';
}
// Vary
const vary = this.headers.get('vary');
if (vary) {
caching.vary = vary.split(',').map(v => v.trim());
if (vary === '*') {
caching.issues.push({
severity: 'medium',
issue: 'Vary: * prevents caching',
impact: 'Response cannot be cached'
});
}
}
return caching;
}
// Analyze compression headers
analyzeCompressionHeaders() {
const compression = {
enabled: false,
encoding: null,
issues: []
};
const contentEncoding = this.headers.get('content-encoding');
if (contentEncoding) {
compression.enabled = true;
compression.encoding = contentEncoding;
// Check for multiple encodings
if (contentEncoding.includes(',')) {
compression.multiple = contentEncoding.split(',').map(e => e.trim());
}
}
// Check Content-Type for compressible resources
const contentType = this.headers.get('content-type');
if (contentType && this.isCompressible(contentType) && !compression.enabled) {
compression.issues.push({
severity: 'medium',
issue: 'Compressible resource served uncompressed',
contentType,
impact: 'Larger transfer size'
});
}
// Check for double compression
if (compression.enabled && contentType && contentType.includes('gzip')) {
compression.issues.push({
severity: 'low',
issue: 'Possible double compression',
impact: 'CPU overhead without benefit'
});
}
return compression;
}
// Check if content type is compressible
isCompressible(contentType) {
const compressible = [
'text/', 'application/json', 'application/javascript',
'application/xml', 'application/svg+xml', 'application/x-font',
'application/vnd.ms-fontobject'
];
return compressible.some(type => contentType.includes(type));
}
// Analyze server headers
analyzeServerHeaders() {
const server = {
software: null,
timing: null,
backend: null,
issues: []
};
// Server header
const serverHeader = this.headers.get('server');
if (serverHeader) {
server.software = serverHeader;
// Parse server info
const parsed = this.parseServerHeader(serverHeader);
server.parsed = parsed;
if (parsed.version) {
server.issues.push({
severity: 'medium',
issue: 'Server version exposed',
version: parsed.version,
impact: 'Attackers can target known vulnerabilities'
});
}
}
// Server-Timing
const serverTiming = this.headers.get('server-timing');
if (serverTiming) {
server.timing = this.parseServerTiming(serverTiming);
}
// Backend indicators
const backendHeaders = [
'x-backend-server',
'x-served-by',
'x-server-id',
'x-proxy-id'
];
backendHeaders.forEach(header => {
const value = this.headers.get(header);
if (value) {
if (!server.backend) server.backend = {};
server.backend[header] = value;
}
});
return server;
}
// Parse server header
parseServerHeader(header) {
const parsed = {
name: null,
version: null,
modules: []
};
// Common patterns
const patterns = {
apache: /Apache(?:\/(\S+))?/i,
nginx: /nginx(?:\/(\S+))?/i,
iis: /Microsoft-IIS(?:\/(\S+))?/i,
nodejs: /Node\.js(?:\/(\S+))?/i,
express: /Express/i
};
for (const [name, pattern] of Object.entries(patterns)) {
const match = header.match(pattern);
if (match) {
parsed.name = name;
if (match[1]) {
parsed.version = match[1];
}
break;
}
}
// Parse modules (e.g., Apache modules)
const modules = header.match(/\(([^)]+)\)/);
if (modules) {
parsed.modules = modules[1].split(' ');
}
return parsed;
}
// Parse Server-Timing header
parseServerTiming(header) {
const timings = [];
// Parse each timing entry
const entries = header.split(',');
entries.forEach(entry => {
const match = entry.match(/(\w+)(?:;(?:dur=(\d+(?:\.\d+)?)))?(?:;desc="([^"]+)")?/);
if (match) {
timings.push({
name: match[1],
duration: match[2] ? parseFloat(match[2]) : null,
description: match[3] || null
});
}
});
return timings;
}
// Analyze custom headers
analyzeCustomHeaders() {
const custom = {
headers: [],
patterns: {
api: [],
debug: [],
internal: [],
tracking: []
}
};
// Define patterns
const patterns = {
api: /^x-api-|api-version|x-ratelimit/i,
debug: /debug|trace|profile/i,
internal: /internal|private|backend/i,
tracking: /request-id|trace-id|correlation-id/i
};
// Check all headers
for (const [header, value] of this.headers) {
// Skip standard headers
if (this.isStandardHeader(header)) continue;
// Categorize custom headers
for (const [category, pattern] of Object.entries(patterns)) {
if (pattern.test(header)) {
custom.patterns[category].push({
header,
value
});
}
}
// Add to custom list
if (header.startsWith('x-') || !this.isCommonHeader(header)) {
custom.headers.push({
header,
value,
category: this.categorizeHeader(header)
});
}
}
return custom;
}
// Check if header is standard
isStandardHeader(header) {
const standard = [
'cache-control', 'content-type', 'content-length', 'date',
'etag', 'expires', 'last-modified', 'location', 'server',
'set-cookie', 'vary', 'content-encoding', 'transfer-encoding'
];
return standard.includes(header);
}
// Check if header is common
isCommonHeader(header) {
const common = [
'accept-ranges', 'age', 'allow', 'content-language',
'content-location', 'content-range', 'pragma', 'retry-after',
'strict-transport-security', 'x-content-type-options',
'x-frame-options', 'x-xss-protection'
];
return common.includes(header);
}
// Categorize custom header
categorizeHeader(header) {
if (header.includes('api')) return 'api';
if (header.includes('limit')) return 'rate-limiting';
if (header.includes('id')) return 'tracking';
if (header.includes('time')) return 'timing';
return 'other';
}
// Calculate overall score
calculateScore() {
let score = 100;
// Security impact
const securityScore = this.analyzeSecurityHeaders().score;
score = Math.min(score, securityScore);
// Issues impact
this.issues.forEach(issue => {
switch (issue.severity) {
case 'high':
score -= 20;
break;
case 'medium':
score -= 10;
break;
case 'low':
score -= 5;
break;
}
});
return Math.max(0, score);
}
// Generate recommendations
generateRecommendations() {
const recommendations = [];
// Security recommendations
const security = this.analyzeSecurityHeaders();
if (security.missing.length > 0) {
recommendations.push({
category: 'security',
priority: 'high',
title: 'Add missing security headers',
headers: security.missing,
implementation: this.generateSecurityHeadersConfig()
});
}
if (security.misconfigured.length > 0) {
recommendations.push({
category: 'security',
priority: 'medium',
title: 'Fix misconfigured security headers',
headers: security.misconfigured
});
}
// Caching recommendations
const caching = this.analyzeCachingHeaders();
if (caching.strategy === 'none') {
recommendations.push({
category: 'performance',
priority: 'medium',
title: 'Implement caching strategy',
suggestion: 'Add Cache-Control headers for better performance'
});
}
// Compression recommendations
const compression = this.analyzeCompressionHeaders();
if (!compression.enabled) {
recommendations.push({
category: 'performance',
priority: 'medium',
title: 'Enable compression',
suggestion: 'Use gzip or brotli compression for text resources'
});
}
// Server recommendations
const server = this.analyzeServerHeaders();
if (server.issues.length > 0) {
recommendations.push({
category: 'security',
priority: 'medium',
title: 'Hide server information',
issues: server.issues
});
}
return recommendations;
}
// Generate security headers configuration
generateSecurityHeadersConfig() {
return {
nginx: `# Nginx security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;`,
apache: `# Apache security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"`,
nodejs: `// Express.js security headers
const helmet = require('helmet');
app.use(helmet({
strictTransportSecurity: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}
}));`
};
}
}
// Analyze current page
const analyzer = new ResponseHeaderAnalyzer();
analyzer.analyzeCurrentPage().then(analysis => {
console.log('Header Analysis:', analysis);
});
Header Configuration Generator
Generate optimal header configurations:
// Header Configuration Generator
class HeaderConfigGenerator {
constructor() {
this.config = {
security: {},
caching: {},
compression: {},
custom: {}
};
}
// Generate complete configuration
generateConfiguration(options = {}) {
const config = {
nginx: this.generateNginxConfig(options),
apache: this.generateApacheConfig(options),
express: this.generateExpressConfig(options),
cloudflare: this.generateCloudflareConfig(options),
vercel: this.generateVercelConfig(options),
netlify: this.generateNetlifyConfig(options)
};
return config;
}
// Generate Nginx configuration
generateNginxConfig(options) {
const {
environment = 'production',
csp = true,
hsts = true,
caching = true,
compression = true
} = options;
let config = `# Nginx Headers Configuration
# Generated: ${new Date().toISOString()}
`;
// Security headers
if (environment === 'production') {
config += `# Security Headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;
`;
if (hsts) {
config += `# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
`;
}
if (csp) {
config += `# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
`;
}
}
// Hide server version
config += `# Hide server information
server_tokens off;
more_clear_headers 'Server';
more_clear_headers 'X-Powered-By';
`;
// Compression
if (compression) {
config += `# Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml+rss application/x-font-ttf font/opentype image/svg+xml image/x-icon;
gzip_disable "msie6";
gzip_comp_level 6;
# Brotli compression (if available)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml+rss application/x-font-ttf font/opentype image/svg+xml image/x-icon;
`;
}
// Caching
if (caching) {
config += `# Caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.(html|xml|json)$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
# API responses
location /api/ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
`;
}
// Custom headers
config += `# Custom headers
add_header X-Request-ID $request_id always;
add_header X-Content-Type-Options "nosniff" always;
# CORS (if needed)
# add_header Access-Control-Allow-Origin "https://example.com" always;
# add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
# add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
# add_header Access-Control-Max-Age "86400" always;
`;
return config;
}
// Generate Apache configuration
generateApacheConfig(options) {
const {
environment = 'production',
csp = true,
hsts = true,
caching = true,
compression = true
} = options;
let config = `# Apache Headers Configuration
# Generated: ${new Date().toISOString()}
<IfModule mod_headers.c>
`;
// Security headers
if (environment === 'production') {
config += ` # Security Headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
`;
if (hsts) {
config += ` # HSTS
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
`;
}
if (csp) {
config += ` # Content Security Policy
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
`;
}
}
// Hide server information
config += ` # Hide server information
Header always unset Server
Header unset X-Powered-By
ServerTokens Prod
ServerSignature Off
</IfModule>
`;
// Compression
if (compression) {
config += `# Compression
<IfModule mod_deflate.c>
# Compress HTML, CSS, JavaScript, Text, XML and fonts
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
# Remove browser bugs
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
Header append Vary User-Agent
</IfModule>
`;
}
// Caching
if (caching) {
config += `# Caching
<IfModule mod_expires.c>
ExpiresActive On
# Images
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# CSS and JavaScript
ExpiresByType text/css "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
# Web fonts
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/otf "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
# HTML and XML
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
</IfModule>
<IfModule mod_headers.c>
# Cache-Control headers
<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf|woff|woff2)$">
Header set Cache-Control "public, immutable, max-age=31536000"
</FilesMatch>
# Don't cache HTML
<FilesMatch "\.(html|htm|xml|json)$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "0"
</FilesMatch>
</IfModule>
`;
}
return config;
}
// Generate Express.js configuration
generateExpressConfig(options) {
const {
environment = 'production',
csp = true,
hsts = true,
caching = true,
compression = true
} = options;
let config = `// Express.js Headers Configuration
// Generated: ${new Date().toISOString()}
const express = require('express');
const helmet = require('helmet');
const compression = require('compression');
const app = express();
`;
// Compression
if (compression) {
config += `// Enable compression
app.use(compression({
filter: (req, res) => {
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
},
level: 6
}));
`;
}
// Security headers with Helmet
config += `// Security headers
app.use(helmet({
contentSecurityPolicy: ${csp ? `{
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://www.google-analytics.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
frameAncestors: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"]
}
}` : 'false'},
hsts: ${hsts ? `{
maxAge: 31536000,
includeSubDomains: true,
preload: true
}` : 'false'}
}));
// Additional security headers
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()');
// Remove X-Powered-By
res.removeHeader('X-Powered-By');
next();
});
`;
// Caching
if (caching) {
config += `// Caching middleware
const setCache = (duration) => {
return (req, res, next) => {
if (req.method === 'GET') {
res.set('Cache-Control', \`public, max-age=\${duration}\`);
} else {
res.set('Cache-Control', 'no-store');
}
next();
};
};
// Static files caching
app.use('/static', setCache(31536000), express.static('public'));
// API no-cache
app.use('/api', (req, res, next) => {
res.set({
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
});
next();
});
`;
}
// Custom headers
config += `// Custom headers
app.use((req, res, next) => {
// Request ID for tracking
const requestId = req.headers['x-request-id'] || generateRequestId();
res.setHeader('X-Request-ID', requestId);
// Server timing
res.setHeader('Server-Timing', \`app;dur=\${Date.now() - req.startTime}\`);
next();
});
// CORS configuration (if needed)
/*
const cors = require('cors');
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
*/
function generateRequestId() {
return Math.random().toString(36).substr(2, 9);
}
module.exports = app;`;
return config;
}
// Generate Cloudflare Workers configuration
generateCloudflareConfig(options) {
return `// Cloudflare Workers Headers
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const response = await fetch(request);
// Create new response with modified headers
const newHeaders = new Headers(response.headers);
// Security headers
newHeaders.set('X-Content-Type-Options', 'nosniff');
newHeaders.set('X-Frame-Options', 'SAMEORIGIN');
newHeaders.set('X-XSS-Protection', '1; mode=block');
newHeaders.set('Referrer-Policy', 'strict-origin-when-cross-origin');
newHeaders.set('Permissions-Policy', 'accelerometer=(), camera=(), geolocation=()');
// HSTS (if HTTPS)
if (request.url.startsWith('https://')) {
newHeaders.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
}
// CSP
newHeaders.set('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';");
// Remove sensitive headers
newHeaders.delete('X-Powered-By');
newHeaders.delete('Server');
// Add custom headers
newHeaders.set('X-Request-ID', crypto.randomUUID());
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
});
}`;
}
// Generate Vercel configuration
generateVercelConfig(options) {
return `{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
},
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
},
{
"key": "Permissions-Policy",
"value": "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
}
]
},
{
"source": "/(.*)",
"has": [
{
"type": "host",
"value": "(?<protocol>https):\\/\\/(.*)"
}
],
"headers": [
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains; preload"
}
]
},
{
"source": "/api/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache, no-store, must-revalidate"
}
]
},
{
"source": "/static/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}`;
}
// Generate Netlify configuration
generateNetlifyConfig(options) {
return `# Netlify Headers Configuration (_headers file)
/*
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self';
https://*
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
/api/*
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
/static/*
Cache-Control: public, max-age=31536000, immutable
/*.js
Cache-Control: public, max-age=31536000, immutable
Content-Type: application/javascript; charset=UTF-8
/*.css
Cache-Control: public, max-age=31536000, immutable
Content-Type: text/css; charset=UTF-8
/*.woff2
Cache-Control: public, max-age=31536000, immutable
Content-Type: font/woff2
Access-Control-Allow-Origin: *`;
}
}
// Generate configurations
const generator = new HeaderConfigGenerator();
const configs = generator.generateConfiguration({
environment: 'production',
csp: true,
hsts: true,
caching: true,
compression: true
});
console.log('Header Configurations:', configs);
Header Testing Suite
Test header configurations:
// Header Testing Suite
class HeaderTester {
constructor() {
this.tests = [];
this.results = {
passed: 0,
failed: 0,
warnings: 0
};
}
// Run all header tests
async runTests(url) {
console.log(`Testing headers for: ${url}`);
const tests = [
this.testSecurityHeaders(url),
this.testCaching(url),
this.testCompression(url),
this.testCORS(url),
this.testServerInfo(url),
this.testCustomHeaders(url)
];
const results = await Promise.all(tests);
// Compile results
const report = {
url,
timestamp: new Date().toISOString(),
tests: results.flat(),
summary: this.results,
score: this.calculateTestScore()
};
return report;
}
// Test security headers
async testSecurityHeaders(url) {
const tests = [];
try {
const response = await fetch(url, { method: 'HEAD' });
const headers = Object.fromEntries(response.headers.entries());
// Test each security header
const securityTests = {
'strict-transport-security': {
name: 'HSTS',
test: (value) => {
if (!value) return { passed: false, error: 'Missing HSTS header' };
const maxAge = value.match(/max-age=(\d+)/);
if (!maxAge || parseInt(maxAge[1]) < 31536000) {
return { passed: false, error: 'HSTS max-age too short' };
}
return { passed: true };
}
},
'x-content-type-options': {
name: 'X-Content-Type-Options',
test: (value) => ({
passed: value === 'nosniff',
error: value ? 'Invalid value' : 'Missing header'
})
},
'x-frame-options': {
name: 'X-Frame-Options',
test: (value) => ({
passed: ['DENY', 'SAMEORIGIN'].includes(value),
error: value ? 'Invalid value' : 'Missing header'
})
},
'content-security-policy': {
name: 'CSP',
test: (value) => {
if (!value) return { passed: false, error: 'Missing CSP header' };
if (value.includes('unsafe-inline') && value.includes('unsafe-eval')) {
return { passed: false, warning: 'CSP contains unsafe directives' };
}
return { passed: true };
}
}
};
for (const [header, config] of Object.entries(securityTests)) {
const value = headers[header];
const result = config.test(value);
tests.push({
category: 'security',
name: config.name,
header,
value: value || 'not set',
...result
});
if (result.passed) this.results.passed++;
else if (result.warning) this.results.warnings++;
else this.results.failed++;
}
} catch (error) {
tests.push({
category: 'security',
name: 'Security Headers Test',
error: error.message,
passed: false
});
this.results.failed++;
}
return tests;
}
// Test caching configuration
async testCaching(url) {
const tests = [];
try {
// Test different resource types
const resources = [
{ path: '/', type: 'html', expectedCache: 'no-cache' },
{ path: '/static/image.jpg', type: 'image', expectedCache: 'max-age=31536000' },
{ path: '/api/data', type: 'api', expectedCache: 'no-store' }
];
for (const resource of resources) {
const testUrl = new URL(resource.path, url).href;
try {
const response = await fetch(testUrl, { method: 'HEAD' });
const cacheControl = response.headers.get('cache-control');
tests.push({
category: 'caching',
name: `Cache test: ${resource.type}`,
url: testUrl,
cacheControl,
passed: this.validateCacheControl(cacheControl, resource.expectedCache)
});
} catch (e) {
// Resource might not exist
tests.push({
category: 'caching',
name: `Cache test: ${resource.type}`,
skipped: true,
reason: 'Resource not found'
});
}
}
} catch (error) {
tests.push({
category: 'caching',
error: error.message,
passed: false
});
}
return tests;
}
// Validate cache control
validateCacheControl(actual, expected) {
if (!actual) return false;
if (expected === 'no-cache') {
return actual.includes('no-cache') || actual.includes('no-store');
}
if (expected === 'no-store') {
return actual.includes('no-store');
}
if (expected.includes('max-age')) {
return actual.includes('max-age');
}
return true;
}
// Test compression
async testCompression(url) {
const tests = [];
try {
const response = await fetch(url, {
headers: {
'Accept-Encoding': 'gzip, deflate, br'
}
});
const encoding = response.headers.get('content-encoding');
const contentType = response.headers.get('content-type');
tests.push({
category: 'compression',
name: 'Compression test',
encoding: encoding || 'none',
contentType,
passed: encoding && ['gzip', 'deflate', 'br'].includes(encoding),
warning: !encoding && this.isCompressible(contentType) ?
'Compressible content served uncompressed' : null
});
} catch (error) {
tests.push({
category: 'compression',
error: error.message,
passed: false
});
}
return tests;
}
// Check if content is compressible
isCompressible(contentType) {
if (!contentType) return false;
const compressible = ['text/', 'application/json', 'application/javascript', 'application/xml'];
return compressible.some(type => contentType.includes(type));
}
// Test CORS configuration
async testCORS(url) {
const tests = [];
try {
// Test preflight request
const response = await fetch(url, {
method: 'OPTIONS',
headers: {
'Origin': 'https://example.com',
'Access-Control-Request-Method': 'POST'
}
});
const corsHeaders = {
'access-control-allow-origin': response.headers.get('access-control-allow-origin'),
'access-control-allow-methods': response.headers.get('access-control-allow-methods'),
'access-control-allow-headers': response.headers.get('access-control-allow-headers'),
'access-control-max-age': response.headers.get('access-control-max-age')
};
tests.push({
category: 'cors',
name: 'CORS configuration',
headers: corsHeaders,
passed: corsHeaders['access-control-allow-origin'] !== null,
info: 'CORS headers present'
});
} catch (error) {
tests.push({
category: 'cors',
name: 'CORS test',
skipped: true,
reason: 'Could not test CORS'
});
}
return tests;
}
// Test server information leakage
async testServerInfo(url) {
const tests = [];
try {
const response = await fetch(url, { method: 'HEAD' });
const sensitiveHeaders = [
'server',
'x-powered-by',
'x-aspnet-version',
'x-aspnetmvc-version',
'x-backend-server'
];
sensitiveHeaders.forEach(header => {
const value = response.headers.get(header);
if (value) {
tests.push({
category: 'server-info',
name: `Information leakage: ${header}`,
header,
value,
passed: false,
error: 'Sensitive information exposed'
});
this.results.failed++;
}
});
if (tests.length === 0) {
tests.push({
category: 'server-info',
name: 'Server information',
passed: true,
info: 'No sensitive headers found'
});
this.results.passed++;
}
} catch (error) {
tests.push({
category: 'server-info',
error: error.message,
passed: false
});
}
return tests;
}
// Test custom headers
async testCustomHeaders(url) {
const tests = [];
try {
const response = await fetch(url);
const headers = Object.fromEntries(response.headers.entries());
// Look for custom headers
const customHeaders = Object.entries(headers)
.filter(([key]) => key.startsWith('x-') &&
!['x-content-type-options', 'x-frame-options', 'x-xss-protection'].includes(key));
if (customHeaders.length > 0) {
tests.push({
category: 'custom',
name: 'Custom headers found',
headers: Object.fromEntries(customHeaders),
passed: true,
info: `${customHeaders.length} custom headers detected`
});
}
} catch (error) {
tests.push({
category: 'custom',
error: error.message,
passed: false
});
}
return tests;
}
// Calculate test score
calculateTestScore() {
const total = this.results.passed + this.results.failed + this.results.warnings;
if (total === 0) return 0;
const score = ((this.results.passed + (this.results.warnings * 0.5)) / total) * 100;
return Math.round(score);
}
}
// Run header tests
const tester = new HeaderTester();
tester.runTests(window.location.origin).then(report => {
console.log('Header Test Report:', report);
});
Best Practices for Response Headers
- Hide Server Information: Remove version numbers and technology indicators
- Implement Security Headers: HSTS, CSP, X-Frame-Options, etc.
- Configure Caching Properly: Different strategies for different resources
- Enable Compression: Gzip/Brotli for text resources
- Set Correct Content-Types: Prevent MIME sniffing attacks
- Use HTTPS Everywhere: Force secure connections
- Implement CORS Carefully: Only allow necessary origins
- Add Request Tracking: X-Request-ID for debugging
- Monitor Header Changes: Headers can reveal infrastructure changes
- Test Regularly: Automated header testing in CI/CD
Common Header Mistakes
- Version Disclosure: Apache/2.4.41, nginx/1.17.0 in Server header
- Technology Leakage: X-Powered-By: PHP/7.2.24
- Missing Security Headers: No HSTS, CSP, or X-Frame-Options
- Cache Confusion: Private data with public cache headers
- CORS Wildcards: Access-Control-Allow-Origin: *
- Debug Headers: X-Debug-Token in production
- Inconsistent Headers: Different headers for same resource type
- Header Injection: Reflecting user input in headers
- Outdated Headers: Using deprecated security headers
- No Compression: Serving large text files uncompressed
Remember: Response headers are your application's metadata. Configure them carefully—they can either strengthen your security posture or expose your vulnerabilities to the world.