htttp.Request.RemoteAddr, X-Real-IP and nginx proxying

2,739 views
Skip to first unread message

Krzysztof Kowalczyk

unread,
Oct 20, 2012, 11:53:01 PM10/20/12
to golang-nuts
In my web app (which uses built-in http server) I sometimes need to
know the ip address of the user. I use http.Request.RemoteAddr for
that.

However, it runs on a server on local port behind nginx proxy and in
that scenario RemoteAddr is localhost i.e. 127.0.0.1

My nginx.conf is
https://github.com/kjk/apptranslator/blob/master/scripts/nginx.conf

I do use "proxy_set_header X-Real-IP $remote_addr;" but this is just
config I copied from somewhere else (I'm not nginx expert).

Is there some nginx config that I can use so that http server would
set http.Request.RemoteAddr "properly" in the proxying scenario or is
the right solution to use value of X-Real-IP header instead of
RemoteAddr?

-- kjk

Dave Cheney

unread,
Oct 20, 2012, 11:56:53 PM10/20/12
to Krzysztof Kowalczyk, golang-nuts
The canonical solution is to use X-Forwarded-For.
> --
>
>
Message has been deleted

Krzysztof Kowalczyk

unread,
Oct 21, 2012, 2:43:04 AM10/21/12
to PeteT, golan...@googlegroups.com
Thanks Pete and Dave, those are good tips.

I did some additional research and here's what I ended up doing (for
posterity and in case someone has tips on how to further improve the
code):

// Request.RemoteAddress contains port, which we want to remove i.e.:
// "[::1]:58292" => "[::1]"
func ipAddrFromRemoteAddr(s string) string {
idx := strings.LastIndex(s, ":")
if idx == -1 {
return s
}
return s[:idx]
}

func getIpAddress(r *http.Request) string {
hdr := r.Header
hdrRealIp := hdr.Get("X-Real-Ip")
hdrForwardedFor := hdr.Get("X-Forwarded-For")
if hdrRealIp == "" && hdrForwardedFor == "" {
return ipAddrFromRemoteAddr(r.RemoteAddr)
}
if hdrForwardedFor != "" {
// X-Forwarded-For is potentially a list of addresses separated with ","
parts := strings.Split(hdrForwardedFor, ",")
for i, p := range parts {
parts[i] = strings.TrimSpace(p)
}
// TODO: should return first non-local address
return parts[0]
}
return hdrRealIp
}

The main point of the code is that "X-Forwarded-For" header can be a
list of ip addresses and I also use "X-Real-Ip" if "X-Forwarded-For"
is not present.

According to http://rod.vagg.org/2011/07/wrangling-the-x-forwarded-for-header/,
I should really use the first non-local ip address from
"X-Forwarded-For" list but I ran out of steam.

-- kjk

On Sat, Oct 20, 2012 at 9:31 PM, PeteT <peter...@ymail.com> wrote:
> When running behind a proxy, wrap your application's mux with a handler that
> modifies the request with the x-fowarded-for header:
>
> type remoteAddrFixup struct {
> h http.Handler
> }
>
> func (h remoteAddrFixup) ServeHTTP(w http.ResponseWriter, r *http.Request) {
> r.RemoteAddr = r.Header.Get("X-Forwarded-For")
> h.h(w, r)
> }
>
> Assuming that you are using the default serve mux, you can do something like
> this in your application main:
>
> var h http.Handler
> if runningBehindProxy {
> h = remoteAddrFixup{http.DefaultServeMux}
> } else {
> h = http.DefaultServeMux
> }
> http.ListenAndServe(addr, h)
>
> --
>
>
Reply all
Reply to author
Forward
0 new messages