logo

tl;dr Node transforms header names to lowercase and even stuffs in a few default headers, if needed.

A few days ago, I was working on a client library for HTTP requests in node. When I wrote a test to assert that the headers I passed in were actually being sent to the server, I saw a few things I didn’t expect.

Header name transformation

Node transforms header names to lowercase. If you pass in a header with the name FOO and the value BAR, the header will present server side as {foo: 'BAR'}.

Default headers

Node supplies some default headers, dependent on the HTTP Method that you’re using. After a bit of experimenting and some spelunking, I made a list.

GET default headers

  1. host
  2. connection

POST default headers

  1. host
  2. connection
  3. content-length

PUT default headers

  1. host
  2. connection
  3. content-length

PATCH default headers

  1. host
  2. connection
  3. content-length

HEAD default headers

  1. host
  2. connection

DELETE default headers

  1. host
  2. connection

CONNECT default headers

  1. host
  2. connection

OPTIONS default headers

  1. host
  2. connection

TRACE default headers

  1. host
  2. connection
  3. content-length

If you’re in the mood to fiddle with this yourself, here’s the test code that I used (I didn’t handle CONNECT well, sue me):

const http = require('http')

let server = {}

function listen(port) {
  return new Promise((resolve, fail) => {
    server = http.createServer(handleRequest)
    server.on('listening', resolve)
    server.on('connect', handleConnection)
    server.listen(port)
  })
}

function close() {
  return new Promise((resolve, reject) => {
    server.close(resolve)
  })
}

function handleRequest(request, response) {
  console.log(request.headers)
  response.end()
}

function handleConnection(request, socket, response) {
  console.log(request.headers)
}

function sendRequest() {
  const options = {
    hostname: 'localhost',
    port: 3000,
    path: '/',
    method: 'GET'
  }

  return new Promise((resolve, reject) => {
    const request = http.request(options, resolve)
    request.on('error', reject)
    request.end()
  })
}

listen(3000).then(sendRequest).then(close)