Client IP Behind Trusted Proxies
Hard · HTTP
Behind a load balancer or reverse proxy, the socket address is the proxy, not the user. Each proxy appends the address it received the request from to the X-Forwarded-For header, so it looks like client, proxy1, proxy2. But anyone can send that header, so Express only believes the parts added by proxies you trust (the trust proxy setting, implemented by the proxy-addr package).
Write getClientIp(remoteAddress, forwardedFor, trust) that returns { ip, ips } the way Express computes req.ip and req.ips:
- Build the address list: first remoteAddress, then the X-Forwarded-For entries from right to left. Entries are separated by commas; trim spaces and skip empty entries. forwardedFor is "" when the header is missing.
- Walk the list from the start. The first address that is NOT trusted is the client, and the addresses after it are dropped. If every address except the last one is trusted, the client is the last one (the leftmost header entry), trusted or not.
- ip is the client address, returned exactly as written. ips is the kept list without remoteAddress, reversed (farthest client first, closest proxy last). It is [] when only the remote address is kept.
trust is one of:
- true: trust every address.
- a number n: trust the first n addresses of the list (the remote address is position 0).
- an array of IPv4 addresses ("10.0.0.1"), CIDR ranges ("10.0.0.0/8") and the preset names "loopback" (127.0.0.0/8), "linklocal" (169.254.0.0/16) and "uniquelocal" (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16).
This is a simplified, IPv4-only version of proxy-addr. A valid address is four decimal numbers from 0 to 255 separated by dots. An IPv4-mapped IPv6 address such as ::ffff:10.0.0.1 (Node reports these on dual-stack sockets) is matched as 10.0.0.1 but still returned as written. Anything else is never trusted. A CIDR a.b.c.d/n contains an address when their first n bits are equal.