How to get remote ip from x-forwarded-for header in Phoenix

In my project hipCV there is a functionality which tracks visits for a publicly listed resume. For the server setup, I have multiple docker containers being load balanced via nginx proxy.

To track the visits, we need the real remote IP address of the request. This IP address can then be used by geoip service, to provide the location.

Since the requests to application server are being proxied, conn.remote_ip address is of the nginx itself and not the original request. The real IP address is also being returned by ngnix, but they come through in the headers x-forwarded-for.

So, all we need to do is to parse this information from headers. To make it simple, I wrote a small utility module which does just that.

defmodule Utils.RemoteIp do
  @spec get(Plug.Conn.t()) :: binary()
  def get(conn) do
    forwarded_for = conn
      |> Plug.Conn.get_req_header("x-forwarded-for")
      |> List.first()

    if forwarded_for do
      forwarded_for
      |> String.split(",")
      |> Enum.map(&String.trim/1)
      |> List.first()
    else
      conn.remote_ip
      |> :inet_parse.ntoa()
      |> to_string()
    end
  end
end

Module is quite simple where it takes a conn struct as input and tries to get the value of the IP address out of x-forwarded-for header. If it is able to find it, it returns that, otherwise the original IP address of the request is returned.

We can use this module anywhere in our controller as long as we have conn struct.

Utils.RemoteIp.get(conn)

There you have it, now you can get the real remote ip from proxied requests without any problems.

Comments