Why is it necessary to know the infrastructure of your hosting provider?
As a web developer, you may have already heard of load-balancer, DDoS mitigator, or more simply proxy or firewall. You may not have bothered until now to understand how they work. But to get the IP address of your visitors, you will need to understand some of the basic mechanisms.
A proxy or firewall is positioned between your web server and your visitor. Its role is to protect your web server, which is certainly configured only to accept connections that have been filtered by the proxy.
In such a configuration, if you run the script we wrote above, you will get an address that doesn't match your IP address, and if you search a little deeper, you will quickly understand that it corresponds to the IP address of the proxy. Indeed, the proxy modifies the client request to make your server believe that it is the request's origin. The proxy thus ensures that all communications pass through it.
In this case, you will have to use the remote_ip method, which is also available in the controllers, verifying that the IP address has been changed by a proxy and returning the client's real address.
request.remote_ip checks all IPs present in the HTTP header, looking for fields generally used by firewalls, load balancers, or proxies, such as HTTP_X_FORWARDED_FOR, and make a guess to return what seems to be the correct visitor's IP address.
Here is how to modify your script:
From now, if you connect to your server, you will see your real IP address.
You should use request.remote_ip only if you are behind a proxy or a firewall, or you would be vulnerable to IP spoofing attacks: as request.remote_ip checks for fields in the HTTP header that proxies usually set, and if you are not behind a proxy, then anyone could manually set a false IP address in the headers. Doing so is as simple as this:
So before choosing between request.ip and request.remote_ip to get your visitors' IP addresses, you need to know a little about your host infrastructure. You could eventually analyze the HTTP headers when a request reaches your application to understand from which header you can get the real IP address.
How to know if the visitor connects through a VPN using Ruby on Rails
If you connect to the Internet through a VPN service and access your IP address detection script, you will find that the address displayed is not your real address but the VPN's exit address.
Since VPN is a service to protect Internet users, they make sure to prevent websites from discovering their users' real IP address. As a result, your script won't determine if the user is behind a VPN server or not, and therefore it will be impossible for you to know if the IP address thus obtained is real.
Detect if a user is using a VPN service
To do this, it is necessary to use a service that analyzes the IP addresses of your visitors and determines, among other things, whether this address corresponds to an exit address of a VPN service. You may be able to develop such a service by yourself, but you would have to update the list of VPNs' list of known IPs constantly, which is extremely time-consuming.
Abstract provides such a service for free, responding very fast and always up to date, through the geolocation API. Just create an account in a few seconds, which gets you your private API key. Then a simple HTTP call allows you to determine if the visitor is behind a VPN.
Doing geolocation in Ruby on Rails
As mentioned above, your visitors' IP address can be used to understand better your audience's demographics, which you can analyze to gain huge advantages in marketing and content targeting.
The less costly approach is to call an external API to translate your visitor's IP addresses to their physical location. Abstract IP Geolocation API provide GPS coordinates, country, city, timezone, and a visitor's currency from its IP address with a simple GET request.
After creating a free account, you obtain your personal API key and can start fetching information. Here is an example of implementation:
Here is an example of a response: