How to fix Error: Invalid trailer header (cannot use trailer with chunked encoding) in Node.js

Node.jsADVANCEDMEDIUMUpdated Dec 9, 2025· 190 views

This error occurs when attempting to use HTTP trailer headers incorrectly with chunked transfer encoding. Trailer headers can only be sent after the message body when chunked encoding is properly enabled and the Trailer header is declared upfront.

Quick fix

Fixes Error: Invalid trailer header (cannot use trailer with chunked encoding)

const http = require('http');
const crypto = require('crypto');

const server = http.createServer((req, res) => {
  // Step 1: Declare which trailers you will send
  res.setHeader('Trailer', 'Content-MD5, X-Checksum');

  // Step 2: Do NOT set Content-Length (forces chunked encoding)
  // Chunked encoding is automatic when Content-Length is not set

  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Transfer-Encoding': 'chunked'  // Explicitly set (optional, automatic)
  });

  // Step 3: Write the body content
  const hash = crypto.createHash('md5');
  const content = 'Hello, World!';

  hash.update(content);
  res.write(content);

  // Step 4: Add trailers AFTER body, BEFORE end()
  const checksum = hash.digest('hex');
  res.addTrailers({
    'Content-MD5': checksum,
    'X-Checksum': checksum.toUpperCase()
  });

  // Step 5: End the response
  res.end();
});

server.listen(3000, () => {
  console.log('Server with trailers running on port 3000');
});
  1. Enable chunked transfer encoding and declare trailers upfront
  2. Remove Content-Length header to enable chunked encoding
  3. Calculate and send checksums or signatures as trailers
  4. Verify client support before using trailers
node.js — error
const http = require('http');
const crypto = require('crypto');
const server = http.createServer((req, res) => {
// Step 1: Declare which trailers you will send
res.setHeader('Trailer', 'Content-MD5, X-Checksum');
// Step 2: Do NOT set Content-Length (forces chunked encoding)
// Chunked encoding is automatic when Content-Length is not set
res.writeHead(200, {
'Content-Type': 'text/plain',
'Transfer-Encoding': 'chunked' // Explicitly set (optional, automatic)
});
// Step 3: Write the body content
const hash = crypto.createHash('md5');
const content = 'Hello, World!';
hash.update(content);
res.write(content);
// Step 4: Add trailers AFTER body, BEFORE end()
const checksum = hash.digest('hex');
res.addTrailers({
'Content-MD5': checksum,
'X-Checksum': checksum.toUpperCase()
});
// Step 5: End the response
res.end();
});
server.listen(3000, () => {
console.log('Server with trailers running on port 3000');
});
Error: Invalid trailer header (cannot use trailer with chunked encoding)

What this error means

The "Invalid trailer header" error is thrown by Node.js when you try to add HTTP trailers to a response without properly setting up chunked transfer encoding. HTTP trailers are special headers sent after the message body, allowing metadata to be included after the content has been transmitted. According to HTTP/1.1 specifications (RFC 7230), trailers have strict requirements: 1. **Chunked Encoding Required**: The response must use Transfer-Encoding: chunked 2. **Trailer Declaration**: You must declare which trailers will be sent using the Trailer header before the body 3. **Timing**: Trailers must be added after the body content but before calling response.end() Node.js enforces these rules strictly. If you try to use addTrailers() on a non-chunked response, or attempt to send trailers without proper setup, this error is thrown to prevent HTTP protocol violations.

Typical symptoms

  • Error thrown when calling response.addTrailers() on a response
  • Message specifically mentions "cannot use trailer with chunked encoding"
  • Occurs when trailers are added without Transfer-Encoding: chunked
  • Response fails to complete when trying to send metadata after the body
  • Issue appears when attempting to send computed metadata (checksums, signatures)
  • Error occurs in streaming scenarios where final metadata is calculated last
  • Problem arises when porting code from other HTTP implementations

Common causes

  1. Calling addTrailers() on a response without chunked transfer encoding enabled
  2. Not setting the Trailer header to declare which trailers will be sent
  3. Attempting to add trailers on HTTP/1.0 responses (which do not support them)
  4. Adding trailers after response.end() has been called
  5. Using addTrailers() with Content-Length header instead of chunked encoding
  6. Trying to send prohibited headers as trailers (Transfer-Encoding, Content-Length, Trailer)
  7. Framework or library automatically setting Content-Length, preventing chunked mode

How to fix "Error: Invalid trailer header (cannot use trailer with chunked encoding)"

1Enable chunked transfer encoding and declare trailers upfront

To use trailers, you must enable chunked encoding and declare which trailers you will send:

javascript
const http = require('http');
const crypto = require('crypto');

const server = http.createServer((req, res) => {
  // Step 1: Declare which trailers you will send
  res.setHeader('Trailer', 'Content-MD5, X-Checksum');

  // Step 2: Do NOT set Content-Length (forces chunked encoding)
  // Chunked encoding is automatic when Content-Length is not set

  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Transfer-Encoding': 'chunked'  // Explicitly set (optional, automatic)
  });

  // Step 3: Write the body content
  const hash = crypto.createHash('md5');
  const content = 'Hello, World!';

  hash.update(content);
  res.write(content);

  // Step 4: Add trailers AFTER body, BEFORE end()
  const checksum = hash.digest('hex');
  res.addTrailers({
    'Content-MD5': checksum,
    'X-Checksum': checksum.toUpperCase()
  });

  // Step 5: End the response
  res.end();
});

server.listen(3000, () => {
  console.log('Server with trailers running on port 3000');
});
2Remove Content-Length header to enable chunked encoding

If Content-Length is set (explicitly or by a framework), trailers won't work. Remove it:

javascript
const http = require('http');
const express = require('express');
const app = express();

// Middleware that prevents Content-Length from being set
app.use((req, res, next) => {
  // Remove Content-Length if set by another middleware
  res.removeHeader('Content-Length');
  next();
});

app.get('/data', (req, res) => {
  // Declare trailers
  res.setHeader('Trailer', 'X-Response-Time');

  const startTime = Date.now();

  // Send response body
  res.write('Processing data...
');

  // Simulate some work
  setTimeout(() => {
    res.write('Done!
');

    // Add trailer with computed metadata
    const duration = Date.now() - startTime;
    res.addTrailers({
      'X-Response-Time': `${duration}ms`
    });

    res.end();
  }, 100);
});

app.listen(3000);
3Calculate and send checksums or signatures as trailers

Trailers are perfect for sending checksums computed during streaming:

javascript
const http = require('http');
const crypto = require('crypto');
const fs = require('fs');

const server = http.createServer((req, res) => {
  const filePath = './large-file.bin';

  // Declare trailer headers upfront
  res.setHeader('Trailer', 'X-SHA256-Checksum, X-Bytes-Sent');
  res.writeHead(200, {
    'Content-Type': 'application/octet-stream',
    'Transfer-Encoding': 'chunked'
  });

  const hash = crypto.createHash('sha256');
  let bytesStreamed = 0;

  const stream = fs.createReadStream(filePath);

  stream.on('data', (chunk) => {
    hash.update(chunk);
    bytesStreamed += chunk.length;
    res.write(chunk);
  });

  stream.on('end', () => {
    // Add trailers with final computed values
    res.addTrailers({
      'X-SHA256-Checksum': hash.digest('hex'),
      'X-Bytes-Sent': bytesStreamed.toString()
    });
    res.end();
  });

  stream.on('error', (err) => {
    console.error('Stream error:', err);
    res.end();
  });
});

server.listen(3000);
4Verify client support before using trailers

Not all clients support trailers. Check the TE (Transfer-Encoding) header:

javascript
const http = require('http');

function clientSupportsTrailers(req) {
  // Check if client accepts trailers via TE header
  const te = req.headers['te'] || '';
  return te.toLowerCase().includes('trailers');
}

const server = http.createServer((req, res) => {
  const useTrailers = clientSupportsTrailers(req);

  if (useTrailers) {
    // Client supports trailers - use them
    res.setHeader('Trailer', 'X-Checksum');
    res.writeHead(200, { 'Content-Type': 'text/plain' });

    const content = 'Data with trailer support';
    res.write(content);

    res.addTrailers({
      'X-Checksum': 'abc123'
    });
  } else {
    // Client doesn't support trailers - use regular headers
    res.writeHead(200, {
      'Content-Type': 'text/plain',
      'X-Checksum': 'abc123'  // Send as regular header instead
    });

    res.write('Data without trailer support');
  }

  res.end();
});

server.listen(3000);
5Avoid prohibited headers in trailers

Certain headers are prohibited from being sent as trailers per HTTP spec:

javascript
const http = require('http');

// These headers are PROHIBITED as trailers:
const PROHIBITED_TRAILERS = [
  'transfer-encoding',
  'content-length',
  'trailer',
  'host',
  'cache-control',
  'max-forwards',
  'te',
  'authorization',
  'set-cookie',
  'content-encoding',
  'content-type',
  'content-range'
];

function isValidTrailerName(name) {
  return !PROHIBITED_TRAILERS.includes(name.toLowerCase());
}

const server = http.createServer((req, res) => {
  res.setHeader('Trailer', 'X-Custom-Metadata');
  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Transfer-Encoding': 'chunked'
  });

  res.write('Response body content
');

  // Safe trailers - custom headers
  const trailers = {
    'X-Custom-Metadata': 'value',
    'X-Server-Timing': '150ms'
  };

  // Validate before adding
  const validTrailers = {};
  for (const [name, value] of Object.entries(trailers)) {
    if (isValidTrailerName(name)) {
      validTrailers[name] = value;
    } else {
      console.warn(`Skipping prohibited trailer: ${name}`);
    }
  }

  res.addTrailers(validTrailers);
  res.end();
});

server.listen(3000);
6Handle trailer errors gracefully with fallbacks

Wrap trailer code in try-catch and provide fallback for HTTP/1.0 clients:

javascript
const http = require('http');

function addTrailersSafely(res, trailers) {
  try {
    // Only works with HTTP/1.1+ and chunked encoding
    if (res.req.httpVersion === '1.0') {
      console.log('HTTP/1.0 client - skipping trailers');
      return false;
    }

    if (!res.hasHeader('trailer')) {
      console.warn('Trailer header not declared - cannot add trailers');
      return false;
    }

    res.addTrailers(trailers);
    return true;
  } catch (err) {
    console.error('Failed to add trailers:', err.message);
    return false;
  }
}

const server = http.createServer((req, res) => {
  // Attempt to use trailers
  const wantTrailers = req.httpVersion !== '1.0';

  if (wantTrailers) {
    res.setHeader('Trailer', 'X-Process-Time');
  }

  res.writeHead(200, { 'Content-Type': 'application/json' });

  const startTime = Date.now();

  // Send body
  res.write(JSON.stringify({ status: 'ok', data: [] }));

  // Try to add trailers
  const processTime = Date.now() - startTime;
  const added = addTrailersSafely(res, {
    'X-Process-Time': `${processTime}ms`
  });

  if (!added) {
    console.log(`Process time: ${processTime}ms (not sent as trailer)`);
  }

  res.end();
});

server.listen(3000);

Advanced notes

HTTP Trailer History: Trailers were introduced in HTTP/1.1 (RFC 2616, now RFC 7230) to allow headers to be sent after the message body. They were designed for cases where metadata can only be computed after streaming the content (checksums, digital signatures, final content length).

Browser Support Limitations: Most modern browsers do not expose trailer headers to JavaScript (even when received). Trailers are primarily useful for:
- Server-to-server communication
- Custom HTTP clients (curl, wget with --trailer support)
- Streaming APIs that need integrity validation
- Monitoring/debugging with computed metadata

Transfer-Encoding Details: When you don't set Content-Length, Node.js automatically uses chunked transfer encoding for HTTP/1.1 responses. Each chunk is prefixed with its size in hexadecimal. After all chunks, trailers can be sent before the final zero-length chunk that signals the end.

Why Trailers Aren't More Common: Despite being in the spec since HTTP/1.1, trailers are rarely used because:
1. Limited client support (browsers don't expose them to JavaScript)
2. Many proxies and CDNs strip or ignore trailers
3. HTTP/2 and HTTP/3 have better mechanisms (like trailing HEADERS frames)
4. Most use cases can be solved with pre-computed headers

HTTP/2 Differences: In HTTP/2, trailers work differently using HEADERS frames with the END_STREAM flag. Node.js's http2 module has a different API (stream.sendTrailers()). The HTTP/1.1 response.addTrailers() only works with the http module.

Security Considerations: Trailers bypass some security middleware that only inspects initial headers. If using trailers for security-sensitive data (authentication, authorization), ensure your security layer processes them correctly.

Practical Use Cases:
- Streaming large files with SHA256 checksums
- Server timing metrics computed after processing
- Digital signatures over streamed content
- ETags computed from actual transmitted bytes
- Compression ratios after encoding completes

Testing Trailers: Test with curl to see trailers: curl -v --http1.1 http://localhost:3000. Most browsers won't show trailers in their developer tools, making debugging difficult without network inspection tools.

Frequently asked questions

What does "Error: Invalid trailer header (cannot use trailer with chunked encodin" mean?
js when you try to add HTTP trailers to a response without properly setting up chunked transfer encoding. HTTP trailers are special headers sent after the message body, allowing metadata to be included after the content has been transmitted. 1 specifications (RFC 7230), trailers have strict requirements: 1.
How do I fix "Error: Invalid trailer header (cannot use trailer with chunked encodin"?
This error occurs when attempting to use HTTP trailer headers incorrectly with chunked transfer encoding. Trailer headers can only be sent after the message body when chunked encoding is properly enabled and the Trailer header is declared upfront.
What causes "Error: Invalid trailer header (cannot use trailer with chunked encodin"?
Calling addTrailers() on a response without chunked transfer encoding enabled Not setting the Trailer header to declare which trailers will be sent Attempting to add trailers on HTTP/1.0 responses (which do not support them)
Is the "Error: Invalid trailer header (cannot use trailer with chunked encodin" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading