Fix request.ip and request.remote_ip inconsistencies

Hi Rails Core!

While adding a reverse proxy to a Rails 4 app, I was surprised to see that request.ip and request.remote_ip had different values.

That’s a gotcha that I’d like to fix.

Here’s a demo app showing how Client-IP and X-Forwarded-For headers values that yield different results.

The difference stems from ActionDispatch::Request#remote_ip being more strict about trusted proxies than Rack::Requests#ip.

I’d like to clean up the inconsistencies, and am looking for a +1 before making a pull request.

Here are some of the changes I’d like to make:

  1. Make ActionDispatch::Request#remote_ip an alias for #ip. Eliminate the developer gotcha.

  2. Move Rails’ customizations in the ActionDispatch::Request subclass to a mixin for Rack::Request. That eliminates developer gotchas if they use a Rack::Request instead of an ActionDispatch::Request.

  3. Add hooks for extending the logic to calculate IP addresses for easier developer customization.

  4. Get rid of ActionDispatch::RemoteIp middleware. It’s no longer needed when all the code to calculate IP addresses lives in Request objects.

Of course I’d maintain backwards compatibility (e.g. config like action_dispatch.trusted_proxies).

I couldn’t find any documentation about why remote_ip and ip would be different. I assume the only reason two different methods exists is b/c Rails’ requests.remote_ip method pre-dates Rack and Rack::Request#ip.

Feedback welcome!

Aaron Suggs

Operations Engineer, Kickstarter

I don't know very well, but isn't ip meant to be for example the nginx
instance that proxied the request and remote_ip the client? Are you
proxing behind something or direct facing the web? If it's direct,
then the values should be the same (from what I think should be,
haven't read the code)

I don’t know very well, but isn’t ip meant to be for example the nginx

instance that proxied the request and remote_ip the client?

Nope. Rack::Request#ip is more complicated than env[‘REMOTE_ADDR’] (which would be the ip address on the other end of the TCP socket, i.e., the nginx server). Rack::Request#ip tries to figure out the “true” client IP by examining env[‘HTTP_CLIENT_IP’] and env[‘HTTP_X_FORWARDED_FOR’] headers. Rails’s remote_ip does the same thing, except it’s more strict/secure about what it considers trusted proxy IPs.

My suggested solution is to move Rails’ extra strictness to the Rack::Request#ip method. That way request.remote_ip and request.ip would return the same value.

Are you

proxing behind something or direct facing the web? If it’s direct,

then the values should be the same (from what I think should be,

haven’t read the code)

I’m proxying. Indeed, if the ruby web server were directly connected to clients, remote_ip and ip would be the same. The problematic edge case is when you use a proxy server that’s not in Rails’ list of trusted proxies (i.e., private IP address, like 10.*).

-Aaron Suggs

I faced exactly the same issue last week when trying to figure out the correct Vagrant client ip for testing purposes.

Since I later decided upon a better strategy for my case which didn’t rely on the client ip but an extra param instead, I didn’t go further to understand why I was not getting the proper id as after reading the code it seemed I was using a trusted ip for the proxy server…

I wasn’t aware of the ip method from Rack though…

But now that you mentioned the IP range 10.x maybe this would explain the weird behavior I got. I think this is the range used in the host for private networks with Vagrant / VirtualBox…