Headers and Proxies

Headers, Client Addresses and Trusted Proxies

req.headers is Node's lower-cased header object; req.get(name) ignores case and understands the Referrer/Referer spelling mess. req.is(type) reports what the body claims to be.

Reading headers and negotiating on the request sideJavaScript
app.get('/hdr', (req, res) => res.json({
  referrer: req.get('Referrer'),          // matches the misspelled Referer header
  isJson: req.is('json'),                 // null when the request has no body
  accepts: req.accepts(['html', 'json']), // best match, or false
  lang: req.acceptsLanguages(['en', 'de'])
}));
Output
$ curl -s .../hdr -H "Accept: application/json;q=0.9, text/html;q=0.8" \
    -H "Accept-Language: de-DE,de;q=0.9" -H "Referer: https://example.com/list"
{"referrer":"https://example.com/list","isJson":null,"accepts":"json","lang":"de"}

req.is('json') returned null, not false, even though the request carried a JSON content type: it answers null when there is no body, so a GET always does. The singular Express 4 24,430 spellings acceptsCharset, acceptsEncoding and acceptsLanguage are gone; only the plural forms remain.

Unconfigured, req.ip is the socket's remote address and req.protocol whatever the socket is — behind a load balancer, both describe the proxy. Proxies forward the truth in X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host, but anyone can send those headers, so Express ignores them until you declare how many hops you control: app.set('trust proxy', 1), a named subnet such as 'loopback', or a CIDR block.

That is a hop count, not a switch. req.socket.remoteAddress is hop 0; Express walks X-Forwarded-For right to left, discarding as many entries as you trust, and the first untrusted address becomes req.ip. Below, one request carrying X-Forwarded-For: 203.0.113.9, 70.41.3.18 and X-Forwarded-Host: api.example.com is replayed against three settings.

One request, three trust-proxy settings, on Express 5.2.1
trust proxy req.protocol req.hostname req.ip req.ips
unset http localhost ::1 []
1 https api.example.com 70.41.3.18 70.41.3.18
2 https api.example.com 203.0.113.9 both, in order

Count carefully. Too low and every client shares the last proxy's address, so a per-IP rate limiter (Rate Limiting) throttles all your users at once; too high — true trusts the leftmost entry unconditionally — and a client can impersonate anyone. A missing trust proxy also leaves req.protocol reporting http under TLS, breaking secure cookies.