This guide explains when and why to use each strategy. The choice of strategy is critical - using the wrong strategy can lead to ip spoofing vulnerabilities.
Table of Contents
Before choosing a strategy, you need to understand your production environment's network topology:
The answers determine which strategy is appropriate and secure for your setup.
socket-level IP: The IP address that a server or proxy observes at the TCP socket level from whatever is directly connecting to it. This is always trustworthy since it cannot be spoofed at the socket level, regardless of whether the connection is from an end user or another proxy in the chain.
private ip addresses: IP addresses reserved for private networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, etc.) that are not routable on the public internet. These typically represent your internal infrastructure components.
reverse proxy / proxy / load balancer / CDN: An intermediary server that sits between clients and your application server, forwarding client requests. Examples include: nginx, Apache, caddy, AWS ALB, Cloudflare, Cloudfront, Fastly, Akamai, Fly.io, Hetzner Load Balancer.
trusted proxy: A reverse proxy under your control or operated by a service you trust, whose IP forwarding headers you can rely on. The key characteristic is that you know what headers it sets and clients cannot bypass it.
ip spoofing: The practice of sending packets with a forged source IP address, or in the context of HTTP headers, setting fake client IP values in headers like X-Forwarded-For
to deceive the application about the request's true origin.
trivially spoofable: Headers or IP values that can be easily faked by any client without special network access. For example, any client can send X-Forwarded-For: 1.2.3.4
in their request.
XFF: Short for X-Forwarded-For
, the most common HTTP header used by proxies to communicate the original client IP address through a chain of proxies. Each proxy typically appends the IP it received the request from.
rightmost-ish: A parsing strategy that extracts IP addresses from the right side of forwarding headers like X-Forwarded-For
, typically skipping a known number of trusted proxies to find the IP added by the first trusted proxy. This is the only trustworthy approach since you control what your trusted proxies add to the header chain (see this article).
leftmost-ish: A parsing strategy that takes the leftmost (first) IP address in forwarding headers, representing the alleged original client. This is highly untrustworthy and vulnerable to spoofing since any client can set arbitrary values at the beginning of these headers (see this article).
When to use:
Why: The socket-level remote address is the actual client IP when there are no intermediaries.
Using this library you can use the remote-addr-strategy, which is provided to be comprehensive, however you can also just use the existing :remote-addr
key in your ring request map.
(strategy/remote-addr-strategy)
;; OR, just fetch the remote addr from the ring request map
(:remote-addr request)
Example network:
Client -> Your Server
Security: Safe - the remote address cannot be spoofed at the socket level.
When to use: You have a trusted reverse proxy that accepts connections directly from clients and sets a specific header with the client IP.
Why: Many Cloud Provider's CDNs and load balancers products provide a single authoritative header with the real client IP. This is the most secure approach when you have such a setup.
Provider headers with trusted values:
cf-connecting-ip
- this is a socket-level IP value (docs)true-client-ip
- also a socket-level IP value, and just for Enterprise customers with backwards compatibility requirements (docs)fly-client-ip
- the socket ip address (docs)x-azure-socketip
- the socket IP address associated with the TCP connection that the current request originated from (docs)ngx_http_realip_module
proxy_set_header X-Real-IP $remote_addr;
Provider headers that require extra config:
These providers offer headers that out of the box are trivially spoofable, and require extra configuration (in your provider's management interface) to configure securely.
true-client-ip
- do trivially spoofable by default, refer to this writeupfastly-client-ip
- trivially spoofable, you must use vcl to configure it fastly docs⛔ Provider headers to avoid:
In nearly all of these cases you are better of using XFF and reasoning about the number of proxies or their network addresses.
x-azure-clientip
- trivially spoofable as it is the leftmost-ish XFF (docs);; Clients connect to your application server *only* through and *directly* through Cloudflare
(strategy/single-ip-header-strategy "cf-connecting-ip")
;; Clients connect to your application server *only* through and *directly* through Fly.io
(strategy/single-ip-header-strategy "fly-client-ip")
Example networks:
Client -> Cloudflare -> Your Server
Client -> nginx -> Your Server
Client -> Load Balancer -> Your Server
Security considerations:
⚠️ Common mistakes:
x-forwarded-for
with this strategy (use rightmost strategies instead)When to use: You have multiple reverse proxies between the internet and the server, all using private IP addresses, and they append to X-Forwarded-For
or Forwarded
headers.
Why: In a typical private network setup, the rightmost non-private IP in the trusted forwarding chain is the real client IP. Private IPs represent your infrastructure.
(strategy/rightmost-non-private-strategy "x-forwarded-for")
;; Using RFC 7239 Forwarded header
(strategy/rightmost-non-private-strategy "forwarded")
Example networks:
Client -> Internet Proxy -> Private Load Balancer -> Private App Server
(1.2.3.4) (10.0.1.1) (10.0.2.1)
X-Forwarded-For: 1.2.3.4, 10.0.1.1
Result: 1.2.3.4 (rightmost non-private)
Security: Secure. Attackers can still spoof the leftmost entries, but not the rightmost non-private IP.
⚠️ Common Mistakes:
When to use: You know exactly how many trusted proxies append IPs to the header, and you want the IP added by the first trusted proxy.
Why: When you have a fixed, known number of trusted proxies, counting backwards gives you the IP that was added by your first trusted proxy (the client IP it saw).
;; Two trusted proxies
(strategy/rightmost-trusted-count-strategy "x-forwarded-for" 2)
;; One trusted proxy
(strategy/rightmost-trusted-count-strategy "forwarded" 1)
Example with count=2:
Client -> Proxy1 -> Proxy2 -> Your Server
(adds A) (adds B)
X-Forwarded-For: A, B
With count=2: Skip 2 from right, return A
Security: Secure, when your proxy count is stable and known.
⚠️ Common Mistakes:
When to use: You know the IPs ranges of all your trusted proxies and want the rightmost IP that's not from a trusted source.
Why: This is the most flexible strategy for complex infrastructures where you know your proxy IPs but they might change within known ranges.
;; You have a VPC where your client-facing load balancer could be any ip address inside the 10.1.1.0/24 subnet (az1) or 10.1.2.0/24 (az2)
(strategy/rightmost-trusted-range-strategy
"x-forwarded-for"
["10.1.1.0/24" "10.1.2.0/24"])
;; Including Cloudflare ranges (these are examples, do not copy them!)
(strategy/rightmost-trusted-range-strategy
"x-forwarded-for"
["173.245.48.0/20" "103.21.244.0/22" ; Example Cloudflare IPv4 ranges
"2400:cb00::/32"]) ; Example Cloudflare IPv6 range
Example:
Client -> Cloudflare -> Your Load Balancer -> App Server
1.2.3.4 173.245.48.1 10.0.1.1
X-Forwarded-For: 1.2.3.4, 173.245.48.1, 10.0.1.1
Trusted ranges: ["173.245.48.0/20", "10.0.0.0/8"]
Result: 1.2.3.4 (rightmost IP not in trusted ranges)
Security: Secure, when ranges are properly maintained.
⚠️ Common Mistakes:
When to use: You have multiple possible network paths to your server and need fallback behavior.
⚠️ Security warning:
Do not abuse ChainStrategy to check multiple headers. There is likely only one header you should be checking, and checking more can leave you vulnerable to IP spoofing.
Each strategy should represent a different network path, not multiple ways to parse the same path.
Why: Real-world deployments can have multiple possible configurations (direct connections or via CDN). Chain strategy tries each approach until one succeeds.
;; Clients can connect view cloudflare or directly to your server
(strategy/chain-strategy
[(strategy/single-ip-header-strategy "cf-connecting-ip")
(strategy/remote-addr-strategy)])
;; Multiple fallback levels
(strategy/chain-strategy
[(strategy/rightmost-trusted-range-strategy "x-forwarded-for" ["10.0.0.0/8"])
(strategy/rightmost-non-private-strategy "x-forwarded-for")
(strategy/remote-addr-strategy)])
Example use cases:
⚠️ Security warning: DO NOT USE UNLESS YOU REALLY KNOW WHAT YOU ARE DOING. The leftmost IP can be trivially spoofed by clients.
When to use: Rarely recommended. Only when you specifically need the IP allegedly closest to the original client (knowing full well that it can be spoofed).
Why: This gives you the "apparent" client IP but offers no security against spoofing.
(strategy/leftmost-non-private-strategy "x-forwarded-for")
(strategy/leftmost-non-private-strategy "forwarded")
Example:
X-Forwarded-For: 1.2.3.4, 2.3.4.5, 192.168.1.1
Result: 1.2.3.4 (leftmost non-private)
Valid use cases:
Before deploying features that rely on the client ip address, deploy this middleware with logging into your production network and verify your strategy works correctly:
curl -H "X-Forwarded-For: 1.2.3.4" ...
nil
when a failure occurs, this could indicate an attack or a configuration problem.x-forwarded-for
when your proxy sets x-real-ip
Remember: the right strategy depends entirely on your specific network configuration. When in doubt, analyze real traffic in a real network setting.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
Ctrl+k | Jump to recent docs |
← | Move to previous article |
→ | Move to next article |
Ctrl+/ | Jump to the search field |