This error occurs when you attempt to set an HTTP header with a value containing characters that violate HTTP header specifications. Node.js strictly validates header values to prevent header injection attacks and protocol violations.
The "Invalid header value" error is thrown by Node.js when you try to set an HTTP response or request header with a value containing illegal characters. HTTP headers must conform to strict RFC 7230 specifications—they can only contain printable ASCII characters (values 0x20-0x7E) and certain control characters. Node.js enforces this validation to prevent: 1. **Header Injection Attacks**: Where attackers inject newlines (\r\n) to add fake headers 2. **Protocol Violations**: Values with invalid characters break HTTP parsing 3. **Security Issues**: Certain character combinations could bypass security filters When you violate these rules, Node.js throws a TypeError with details about the offending header and character.
Always validate header values to ensure they contain only safe characters:
const http = require('http');
// Unsafe - user input directly in header
const req = http.request({
hostname: 'example.com',
headers: {
'X-Custom': userInput // DANGEROUS!
}
});
// Safe - validate first
function isValidHeaderValue(value) {
// Allow only printable ASCII + tabs (0x20-0x7E and 0x09)
return /^[ \x20-\x7E]*$/.test(value);
}
const sanitized = String(userInput).trim();
if (!isValidHeaderValue(sanitized)) {
throw new Error('Invalid header value');
}
const req = http.request({
hostname: 'example.com',
headers: {
'X-Custom': sanitized // Safe
}
});Strip dangerous characters like newlines that enable header injection:
const http = require('http');
const express = require('express');
const app = express();
app.get('/api/proxy', (req, res) => {
// Unsafe - user input directly in header
// const userAgent = req.query.ua; // Could be: "test\r\nX-Injected: value"
// Safe - remove newlines and control characters
const userAgent = String(req.query.ua || '')
.replace(/[\r\n]/g, '') // Remove newlines
.replace(/[^\x20-\x7E\t]/g, ''); // Keep only valid chars
const options = {
hostname: 'external-api.com',
headers: {
'User-Agent': userAgent || 'MyApp/1.0' // Safe default
}
};
const request = http.request(options, (response) => {
res.writeHead(response.statusCode, response.headers);
response.pipe(res);
});
request.on('error', (err) => {
res.status(500).json({ error: err.message });
});
request.end();
});For headers that legitimately need special characters, use URL encoding:
const http = require('http');
const querystring = require('querystring');
// Example: passing JSON or complex data in headers
const data = {
userId: 123,
action: 'view',
timestamp: Date.now()
};
// Unsafe - raw JSON has characters that violate headers
// const header = JSON.stringify(data); // Contains {, }, :, ", etc.
// Safe - encode as URL parameter
const encoded = querystring.stringify(data);
// Result: "userId=123&action=view×tamp=1234567890"
const options = {
hostname: 'api.example.com',
path: '/data',
headers: {
'X-Data': encoded // Safe - only alphanumeric, =, &
}
};
const req = http.request(options, (res) => {
console.log('Status:', res.statusCode);
});
req.end();
// On the receiving end, decode:
const received = querystring.parse(req.headers['x-data']);
console.log(received.userId); // "123"For headers containing binary or complex data, encode as Base64:
const http = require('http');
// Complex data that might contain invalid characters
const binaryData = Buffer.from('\x00\x01\x02\x03', 'binary');
// Unsafe - setting raw binary data
// headers: { 'X-Data': binaryData } // Will fail
// Safe - encode to base64
const encoded = binaryData.toString('base64');
const options = {
hostname: 'api.example.com',
headers: {
'X-Data': encoded, // "AAEQAQ=="
'X-Data-Encoding': 'base64'
}
};
const req = http.request(options, (res) => {
console.log('Response received');
});
req.on('error', (err) => {
console.error('Error:', err.message);
});
req.end();
// On the receiving end:
app.get('/api/data', (req, res) => {
if (req.headers['x-data-encoding'] === 'base64') {
const decoded = Buffer.from(req.headers['x-data'], 'base64');
console.log(decoded); // Original binary data
}
res.send('OK');
});Wrap header-setting code in try-catch blocks to gracefully handle validation errors:
const http = require('http');
function setHeaderSafely(res, name, value, defaultValue = '') {
try {
// Validate before setting
const sanitized = String(value).trim();
// Check for newlines
if (sanitized.includes('\n') || sanitized.includes('\r')) {
console.warn(`Header '${name}' contains newlines, using default`);
res.setHeader(name, defaultValue);
return;
}
// Try to set
res.setHeader(name, sanitized);
} catch (err) {
console.error(`Failed to set header '${name}': ${err.message}`);
// Use safe default instead of crashing
if (defaultValue) {
res.setHeader(name, defaultValue);
}
}
}
// Usage in Express
const express = require('express');
const app = express();
app.get('/api/data', (req, res) => {
setHeaderSafely(res, 'X-User-ID', req.query.userId, 'unknown');
setHeaderSafely(res, 'X-Request-ID', req.headers['x-request-id']);
res.json({ status: 'ok' });
});Consider using libraries that handle header validation safely:
// Using express-validator for header validation
const express = require('express');
const { validationResult, header } = require('express-validator');
const app = express();
// Validate custom headers on incoming requests
app.get('/api/data',
header('X-Custom-Header').trim().escape().optional(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
res.json({ received: req.headers['x-custom-header'] });
}
);
// Using a sanitization utility
function createSafeHeader(name, value) {
// Remove control characters and newlines
const sanitized = String(value)
.replace(/[\r\n]/g, '') // Remove newlines
.replace(/[^\x20-\x7E\t]/g, '') // Keep only valid ASCII
.trim();
if (!sanitized) {
throw new Error(`Header '${name}' became empty after sanitization`);
}
if (sanitized.length > 8000) {
throw new Error(`Header '${name}' exceeds maximum length`);
}
return { name, value: sanitized };
}
module.exports = { createSafeHeader };RFC 7230 Header Compliance: HTTP headers are defined by RFC 7230 and must contain only token characters (alphanumeric + certain symbols like hyphen) for the name, and field-content (printable ASCII + spaces + tabs) for the value. Node.js strictly enforces this to comply with the specification and prevent security issues.
Header Injection Prevention: This validation is crucial for preventing HTTP Response Splitting attacks (CVE-2009-1891) where attackers inject \r\n sequences to add fake headers or inject content. Strict header validation in Node.js 14+ makes these attacks impossible.
Maximum Header Sizes: Node.js has built-in limits on header size (8KB total per header). Some headers like Cookie, User-Agent, or Authorization can legitimately exceed this. For large values, consider alternative mechanisms like POST body parameters or token-based sessions.
Framework Differences: Express.js passes through Node.js's native validation, so res.setHeader() enforces the same rules. Other frameworks like Fastify or Hapi have similar enforcement. Always validate early in your data pipeline.
Common Invalid Characters: Besides newlines (\r, \n), be cautious with:
- Control characters (0x00-0x1F except tab/0x09)
- Non-ASCII characters (>0x7F)
- Unescaped special characters in unquoted values
Best Practice: Treat all external input (query params, user input, database values) as untrusted. Always validate and sanitize before using in HTTP headers. Consider using allowlists (specific character sets) rather than blocklists.
Testing: Test header validation by attempting to inject newlines in your unit tests: 'value\r\nX-Injected: true'. If your code doesn't reject this, it's vulnerable. Use tools like BURP Suite for security testing.
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files
Error: cluster.fork() failed (cannot create child process)
cluster.fork() failed - Cannot create child process