How to get a visitor's IP address in Ruby

Last Updated Dec 29, 2020
Emma Jagger

Engineer, maker, Google alumna, CMU grad

For mail to arrive in the right recipient's mailbox, the exact address, including country, city, zip code, and street must be specified. This way, the post office and its employees know where the mail should be sent and how to route it. The same is true on the Internet: any device connected requires an address to communicate with other devices. This address is known as the IP address.

Thus all visitors who visit your website are identified by their IP address, which is mandatory for the exchange of data, but can also serve several other purposes.

What can I do with a visitor's IP address?

Web servers automatically record the activity of your visitors by identifying them by their IP address. These activities can be found in the server's log files, which are extremely useful to understand their behavior on your site and to optimize its structure and the content of your pages.

But IP addresses also make it possible to identify with some precision the geographical position of your visitors. This is called geolocation.

What if most of your visitors are coming from a non-English speaking country while your site is entirely in English? Geolocation allows you to detect such a situation and know which country your visitors are coming from. With such information, you can decide to translate your site, offer these visitors content in their language, or review your linking profile and improve your SEO to get better natural traffic.

How to get a visitor IP address in Ruby on Rails

The most obvious solution is to call request.ip, which returns the client's IP address. However, when you deploy your application in production and depending on your host's infrastructure, you may get an incorrect result.

For example, if your website is behind a reverse proxy, every call to request.ip will return the proxy's IP address, not the visitor's.

Luckily the request object provides another interesting method: remote_ip. It has to be understood that, even if request is an ActionDispatch::Request object, its method remote_ip is actually a proxy for the ActionDispatch::RemoteIp middleware.

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.

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:


curl -H "X-Forwarded-For: 5.5.5.5" http://your.website.com

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.

Doing geolocation in Ruby

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:


require 'net/http'
require 'net/https'

def make_abstract_request(api_key, ip_address)
  uri = URI('https://ipgeolocation.abstractapi.com/v1/?api_key=#{api_key}&ip_address=#{ip_address}')

  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER

  request =  Net::HTTP::Get.new(uri)

  response = http.request(request)
  puts "Status code: #{ response.code }"
  puts "Response body: #{ response.body }"
rescue StandardError => error
  puts "Error (#{ error.message })"
end

api_key = "THE_API_KEY_IN_YOUR_DASHBOARD"
ip_address = "IP_ADDRESS"

make_abstract_request(api_key, ip_address)

Here is an example of a response:


{
  "ip_address": "17.52.21.100",
  "city": "Cupertino",
  "city_geoname_id": 5341145,
  "region": "California",
  "region_iso_code": "CA",
  "region_geoname_id": 5332921,
  "postal_code": "95014",
  "country": "United States",
  "country_code": "US",
  "country_geoname_id": 6252001,
  "country_is_eu": false,
  "continent": "North America",
  "continent_code": "NA",
  "continent_geoname_id": 6255149,
  "longitude": -122.03,
  "latitude": 37.3219,
  "security": {
    "is_vpn": false
  },
  "timezone": {
    "name": "America/Los_Angeles",
    "abbreviation": "PST",
    "gmt_offset": -8,
    "current_time": "01:43:33",
    "is_dst": false
  },
  "flag": {
    "emoji": "🇺🇸",
    "unicode": "U+1F1FA U+1F1F8",
    "png": "https://static.abstractapi.com/country-flags/US_flag.png",
    "svg": "https://static.abstractapi.com/country-flags/US_flag.svg"
  },
  "currency": {
    "currency_name": "USD",
    "currency_code": "USD"
  },
  "connection": {
    "autonomous_system_number": 714,
    "autonomous_system_organization": "Apple Inc.",
    "connection_type": "Corporate",
    "isp_name": "Apple Inc.",
    "organizaton_name": "Apple Inc"
  }
}

Abstract's IP Geolocation API comes with Ruby libraries, code snippets, guides, and more.
Get started