Skip to content

Instantly share code, notes, and snippets.

@qerub
Created June 12, 2012 15:06
Show Gist options
  • Save qerub/2918060 to your computer and use it in GitHub Desktop.
Save qerub/2918060 to your computer and use it in GitHub Desktop.
[Clojure] Ring middleware for `X-Forwarded-For` [:remote-addr rewrite]
(ns ring.middleware.x-forwarded-for
(:use [clojure.string :only (split)]))
(defn wrap-x-forwarded-for [handler]
(fn [request]
(if-let [xff (get-in request [:headers "x-forwarded-for"])]
(handler (assoc request :remote-addr (last (split xff #"\s*,\s*"))))
(handler request))))
@kaiwaldron
Copy link

kaiwaldron commented Jun 3, 2021

Just a note/warning for folks who might come across this as it's one of the first search results in Google and since I've seen this snippet, or some variation of this snippet, in a few different code bases. If your intention is to get the originating client IP address you want to get the first IP from X-Forwarded-For, not the last (as above). This code will work fine if you only ever go through a single proxy/load balancer. But if you end up going through multiple it will return the last proxy's IP, which is probably not what you want. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For for more details.

@qerub
Copy link
Author

qerub commented Jun 3, 2021

@kaiwaldron: Thanks, that's a good warning. X-Forwarded-For is tricky since it's an in-band signal about an out-of-band/meta concept (the client IP address of the HTTP request). I think I chose last and not first when I created this gist 9 years ago because it contains the address added by the last proxy server in the chain that is often a reverse proxy just in front of the application and the only one that can be trusted. One risk of just switching to first is that a malicious client might send a X-Forwarded-For with a fake address that propagates all the way to the application that then trusts the fake address. Ideally your edge proxy server should strip X-Forwarded-For coming from the internet (as you can't trust it). Many web framework's include a configuration parameter to control which proxy servers (identified by IP address) to trust, e.g. server.tomcat.remoteip.internal-proxies in Spring Boot. I'm writing this just to make sure that nobody reads your tip and switches to first without having the full picture and investigating how their proxy servers behave.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment