[hunchentoot-devel] real-remote-addr and proxy chains

Robert J. Macomber tbnl at rojoma.com
Thu Nov 2 18:15:56 UTC 2006

The real-remote-addr function currently returns the value of the
x-forwarded-for header if it's set, or remote-addr if it's not.  In
the case of chains of proxies, this gives unexpected results as each
proxy appends the address it's proxying for onto the end of the list.

Since I imagine this function is intended to be used in situations
where Hunchentoot is sitting behind proxies of its own, I've written a
function to split things up to give a particular entry in this chain.
Most of the time, I imagine you'd just want the address added by the
closest proxy but if (for example) you're behind mod_proxy behind
squid, this function can tell you the address of the agent that hit
the squid server.

I'm not actually quite sure what to call this function.  Originally at
my local site I'd called it "real-remote-addr", replacing
Hunchentoot's own function, but the name's not sitting easily with me.
For one thing, it's got a different API since it takes the number of
hops along the proxy chain to look, and for another it returns NIL if
you fall off the end of the chain (I'm not quite sure that's correct,
but it seems to me to fail better than DWIMly returning the last
available address, since values supplied by the user's original
request are useless anyway).  Anyway, here it is, still with the name

(defvar *proxy-count* 0
  "The length of the chain of server-side proxies in front of

(defun real-remote-addr (&optional (nth *proxy-count*) (request *request*))
  "Returns the address of the NTH host in the chain of proxies
that set the X-Forwarded-For header.  0 is the address of the
last proxy (or the client, if there are no proxies), 1 the
address of the second proxy (or client if there is only one), and
so forth."
  (if (zerop nth)
      (remote-addr request)
      (let* ((proxies (loop with xff = (header-in :x-forwarded-for request)
                            for pos = 0 then (1+ comma)
                            for comma = (position #\, xff :start pos)
                            collect (string-trim " " (subseq xff pos
                            while comma))
             (position (- (length proxies) nth)))
        (if (minusp position)
            (nth position proxies)))))
Robert Macomber
tbnl at rojoma.com

More information about the Tbnl-devel mailing list