request.ip
request.ip is the basic ip discovery provided by Rack::Request out of the box. Its current definition can be found at https://github.com/rack/rack/blob/master/lib/rack/request.rb .
The next algorithm is to check the REMOTE_ADDR header for any untrusted IP addresses, and if it finds one, it selects the first one . The "trusted" IP addresses in this case are IP addresses from reserved ranges of private subnets , but note that this matches the regular expression, which is probably not the best way to do this. If there is no unreliable REMOTE_ADDR , then it looks at the HTTP_X_FORWARDED_FOR header and selects the last unreliable. If none of them shows to anyone, it returns to raw REMOTE_ADDR , which is probably 127.0.0.1.
request.remote_ip
request.remote_ip is the improved IP discovery provided by ActionDispatch::Request (which inherits from Rack::Request ). This is the code indicated in the question. As you can see, it returns to request.ip if action_dispatch.remote_ip not set to @env . This is done using the RemoteIp , which is included on the Rails stack by default. You can see its source at https://github.com/rails/rails/blob/4-2-stable/actionpack/lib/action_dispatch/middleware/remote_ip.rb .
The RemoteIp , if enabled, provides the following additional functions:
- Provides optional but standard IP spoofing detection.
- Allows you to filter the configuration proxy addresses instead of relying only on the default values.
- Uses the
IPAddr class to actually test IP ranges, rather than relying on a fragile regex. - Uses
HTTP_CLIENT_IP as a source of potential IP addresses.
The algorithm is similar to request.ip , but slightly different. It uses HTTP_X_FORWARDED_FOR from the last to the first, then HTTP_CLIENT_IP from the last to the first, and then finally the last record of REMOTE_ADDR . It puts everything in the list and filters the proxy, choosing the first remaining one.
IP Spoofing Detection
The IP spoofing detection provided by RemoteIp is not particularly powerful, all it does is throw an exception if the last HTTP_CLIENT_IP not in HTTP_X_FORWARDED_FOR . This is not necessarily a sign of an attack, but it is probably a symptom of an incorrect configuration or combination of proxies using different conventions that do not lead to a consistent result.
What to use
In a simple installation where your proxies are local or on private subnets, you can probably leave with request.ip , but request.remote_ip should be considered the best choice overall. If you use proxies with public Internet routing (for example, many CDNs), then RemoteIp can be configured to give you the correct client IP addresses out of the box, whereas request.ip will only be correct if you can set your proxy server up REMOTE_ADDR correct.
Secure configuration
Now turn to Tim Coulter's comment on spoofing. He is definitely right, you should be worried, but he is wrong that you can be faked if by default you are behind nginx or haproxy. RemoteIp designed to prevent spoofing by selecting the last IP address in the chain. The X-Forwarded-For specification indicates that each proxy attaches the requester’s IP address to the end of the chain. By filtering whitelisted proxies, the last record guarantees the client IP address written by your first whitelisted proxy. Of course, there is one caveat that you really should run a proxy server that always installs / adds X-Forwarded-For , so Tim’s advice should actually be the opposite: use request.remote_ip only when proxy.
How to configure for public IP protocols
This is all fine and good, but ActionDispatch::RemoteIp already on the middleware stack by default. How to reconfigure it to add a CIDR proxy server ?!
Add this to your application.rb :
check_spoofing = true proxies = ["23.235.32.0/20", "203.57.145.0/24"] proxies += ActionDispatch::RemoteIp::TRUSTED_PROXIES config.middleware.swap ActionDispatch::RemoteIp, ActionDispatch::RemoteIp, true, proxies