NodeJSHTTP Headers

HTTP Headers

Headers are the metadata that accompanies every request and response — content type, length, caching rules, authentication, cookies, CORS permissions, and more. They're key–value pairs sent before the body. Getting headers right is what makes content render correctly, caches behave, and security protections engage. This page covers reading them, setting them, and the ones that matter most.

Reading request headers

JS
createServer((req, res) => {
  // Node lowercases all incoming header names:
  const contentType = req.headers['content-type']
  const accept      = req.headers['accept']
  const auth        = req.headers['authorization']
  const host        = req.headers['host']

  res.end('ok')
}).listen(3000)
Incoming header names are always lowercase
Access `req.headers['content-type']`, never `'Content-Type'` — Node normalizes names to lowercase since HTTP header names are case-insensitive. Values are kept as the client sent them. Repeatable headers may be arrays (notably `set-cookie`); most are single strings.
Setting response headers

JS
// One at a time:
res.setHeader('Content-Type', 'application/json')
res.setHeader('Cache-Control', 'no-store')

// Read/remove before sending:
res.getHeader('Content-Type')      // 'application/json'
res.removeHeader('X-Powered-By')

// Or all at once with the status:
res.writeHead(200, {
  'Content-Type': 'text/html; charset=utf-8',
  'Cache-Control': 'public, max-age=3600',
})
Set every header before writing the body
As covered in [Request & Response](/nodejs/request-response), once you write body data the headers are flushed and locked — later `setHeader`/`writeHead` calls throw `ERR_HTTP_HEADERS_SENT`. Decide all headers up front. Check `res.headersSent` if you're unsure whether it's too late (useful in error handlers).
The essential headers

Header

Direction

Purpose

Content-Type

Both

Media type of the body (+ charset)

Content-Length

Both

Body size in bytes

Accept

Request

Media types the client wants

Authorization

Request

Credentials (Bearer token, Basic)

Cache-Control

Both

Caching rules

Set-Cookie / Cookie

Resp / Req

Cookies out / in

Location

Response

Target for redirects / created resource

Content-Encoding

Response

Compression (gzip, br)

ETag / Last-Modified

Response

Validators for conditional requests

Content-Type and charset
Wrong (or missing) Content-Type breaks rendering
The `Content-Type` tells the browser how to interpret the body. Send HTML as `text/plain` and the browser shows raw tags; send UTF-8 text without `; charset=utf-8` and non-ASCII characters can garble. Always set it explicitly and include the charset for text: `text/html; charset=utf-8`. For APIs, `application/json` is expected — clients may refuse to parse otherwise.

Content

Content-Type

HTML

text/html; charset=utf-8

JSON

application/json

Plain text

text/plain; charset=utf-8

Form (urlencoded)

application/x-www-form-urlencoded

File upload / multipart

multipart/form-data

Binary download

application/octet-stream

Caching headers

JS
// Cache a static asset for a day, allow shared caches:
res.setHeader('Cache-Control', 'public, max-age=86400')

// Never cache (sensitive/personalized):
res.setHeader('Cache-Control', 'no-store')

// Validators enable cheap 304 responses:
res.setHeader('ETag', '"abc123"')
res.setHeader('Last-Modified', new Date(mtime).toUTCString())
ETag + conditional requests = free bandwidth savings
Send an `ETag` (a content fingerprint) or `Last-Modified`, and the client echoes it back next time via `If-None-Match`/`If-Modified-Since`. If unchanged, you reply `304 Not Modified` with **no body** — the client reuses its cached copy. This is how the web avoids re-downloading unchanged resources. `Cache-Control` governs *whether/how long* to cache; validators govern *revalidation*.
CORS headers

Browsers block cross-origin requests by default. To allow them, the server sends Access-Control-Allow-* headers — and must answer the OPTIONS preflight:

JS
function setCors(res) {
  res.setHeader('Access-Control-Allow-Origin', 'https://app.example.com')
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
}

createServer((req, res) => {
  setCors(res)
  if (req.method === 'OPTIONS') {       // preflight
    res.writeHead(204).end()
    return
  }
  res.end('data')
}).listen(3000)
`Access-Control-Allow-Origin: *` plus credentials is forbidden — and risky
The wildcard `*` allows any site to call your API. The browser will **reject** combining `*` with credentialed requests (cookies/Authorization) — you must echo a specific origin instead. More broadly, don't reflexively send `*`; scope CORS to the origins you actually trust, or you expose authenticated endpoints to every website.
Security headers

Header

Protects against

Strict-Transport-Security

Forces HTTPS (downgrade attacks)

X-Content-Type-Options: nosniff

MIME-type sniffing

Content-Security-Policy

XSS / injection

X-Frame-Options: DENY

Clickjacking

Referrer-Policy

Leaking URLs to other sites

Remove `X-Powered-By` and consider a security-headers library
Node/Express advertise `X-Powered-By` by default, handing attackers a free fingerprint — remove it (`res.removeHeader('X-Powered-By')` or `app.disable('x-powered-by')`). For the full set of protections, the `helmet` middleware sets sensible security headers in one line; reproducing them by hand is error-prone.
Cookies

JS
// Set a secure, HTTP-only session cookie:
res.setHeader('Set-Cookie',
  'sessionId=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600; Path=/')

// Multiple cookies → pass an array:
res.setHeader('Set-Cookie', ['a=1; Path=/', 'b=2; Path=/'])
Always set `HttpOnly`, `Secure`, and `SameSite` on session cookies
`HttpOnly` blocks JavaScript from reading the cookie (mitigates XSS token theft), `Secure` sends it only over HTTPS, and `SameSite` curbs CSRF by limiting cross-site sending. A session cookie missing these is a security liability. Note `Set-Cookie` is the one response header you commonly send as an **array** for multiple cookies.
Next
Direct requests to the right handler based on method and path: [Basic Routing (No Framework)](/nodejs/routing-basics).