Regional Geolocation: 3 Methods To Try

For one of my personal projects, an affiliate website called Casino Cabbie, one of the bigger challenges has been geolocating users to the regional level in order to show them casinos which operate in their state. Here are the three methods I've used so far.

Method 1: ipapi.co

  • Price: free for up to 30,000 requests per month
  • Pros: easy to implement, great support, works on any hosting
  • Cons: sites with high traffic will need a paid plan, pagespeed negatively affected by the external request

The first method I found was to use an IP API to compare the user's IP address to a database and return the region. I used ipapi.co, which allows 30,000 free requests per month - support is also very helpful.

Most of the code below is based on the WPDevDesign tutorial for adding a State condition to Oxygen. Drop it into a code snippet or custom plugin, and you'll be able to return the user location with the function smoothw_geolocation1().

<?php
function smoothw_geolocation1() {
    // set ipapi API key - get from https://ipapi.co/
    $key = 'abc123';

    // get user IP address
    if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { // check ip from share internet
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif ( ! empty ( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { // to check ip is pass from proxy
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    } else {
        $ip = $_SERVER['REMOTE_ADDR'];
    }

    // access API
    $url_json = wp_safe_remote_get('https://ipapi.co/' . $ip . '/json/?key=' . $key);

    // check for error
    if( is_wp_error( $url_json ) ) {
        return false; // Bail early
    }

    // json to php
    $json_raw = wp_remote_retrieve_body( $url_json ); // retrieve json object
    $json_decoded = json_decode($json_raw); // decode json to php

    // get specifics
    $region = $json_decoded->region_code; // get region name
    $country = $json_decoded->country_code_iso3; // get 3-letter country code

    // return the one you want, comment out the other
    return $region;
    // return $country;
}
?>

Method 2: Cloudways GEOIP

  • Price: included in Cloudways hosting (starts at $10/month)
  • Pros: faster than using an external service, no limit on requests
  • Cons: requires support to set up if you want regional geolocation, requires Cloudways hosting

After a while, I realised that Cloudways (affiliate link) offer built in GEOIP with their managed servers:

Regional Geolocation: 3 Methods To Try 1

Since I was using Cloudways already, it made sense to try and use it. After a fair bit of back and forth with the support team, we managed to get the necessary database installed for regional geolocation (if you just want to geolocate to the country level, then it works out of the box). I then used the following code to fetch what I needed:

<?php
function smoothw_geolocation2() {
    // server side GEOIP from Cloudways - https://www.cloudways.com/en/?id=557531
	$geoip_record = geoip_record_by_name($_SERVER['REMOTE_ADDR']);

    /* what you can get out of $geoip_record
    //
    continent_code − Two letter continent code
    country_code − Two letter country code
    country_code3 − Three letter country code
    country_name − The country name
    region − The region code
    city − The city
    postal_code − The Postal Code, FSA or Zip Code
    latitude − The Latitude as signed double
    longitude − The Longitude as signed double
    dma_code − Designated Market Area code (USA and Canada only)
    area_code − The PSTN area code
    */

	$state_code  = $geoip_record['region']; // returns region numerical code
	$states = array(
		'01'  =>  'AL',
		'02'  =>  'AK',
		'04'  =>  'AZ',
		'05'  =>  'AR',
		'06'  =>  'CA',
		'08'  =>  'CO',
		'09'  =>  'CT',
		'10'  =>  'DE',
		'11'  =>  'DC',
		'12'  =>  'FL',
		'13'  =>  'GA',
		'15'  =>  'HI',
		'16'  =>  'ID',
		'17'  =>  'IL',
		'18'  =>  'IN',
		'19'  =>  'IA',
		'20'  =>  'KS',
		'21'  =>  'KY',
		'22'  =>  'LA',
		'23'  =>  'ME',
		'24'  =>  'MD',
		'25'  =>  'MA',
		'26'  =>  'MI',
		'27'  =>  'MN',
		'28'  =>  'MS',
		'29'  =>  'MO',
		'30'  =>  'MT',
		'31'  =>  'NE',
		'32'  =>  'NV',
		'33'  =>  'NH',
		'34'  =>  'NJ',
		'35'  =>  'NM',
		'36'  =>  'NY',
		'37'  =>  'NC',
		'38'  =>  'ND',
		'39'  =>  'OH',
		'40'  =>  'OK',
		'41'  =>  'OR',
		'42'  =>  'PA',
		'44'  =>  'RI',
		'45'  =>  'SC',
		'46'  =>  'SD',
		'47'  =>  'TN',
		'48'  =>  'TX',
		'49'  =>  'UT',
		'50'  =>  'VT',
		'51'  =>  'VA',
		'53'  =>  'WA',
		'54'  =>  'WV',
		'55'  =>  'WI',
		'56'  =>  'WY',
	);
	
	if($state_code !== '00') {
		$region = $states[$state_code];
	} else {
		$region = '';
	}

    $country = $geoip_record['country_code3']; // 3-letter country code

    // return the one you want, comment out the other
    return $region;
    // return $country;
}
?>

As before, drop it into a code snippet or custom plugin, and you'll be able to return the user location with the function smoothw_geolocation2().

Method 3: Cloudflare Worker

  • Price: free for up to 100,000 requests per day
  • Pros: Cloudflare does the heavy lifting, works with any host
  • Cons: more complex to set up for beginners, adds approximately 800-1200ms to loading speed

Since I wanted to move my site from Cloudways to Gridpane, I needed to find another solution. Which led me to using Cloudflare Workers to add the region data to the server header, largely inspired by this tutorial from Dwight Watson. Here's the step-by-step:

  1. If you haven't already, add your website to Cloudflare on a free plan
  2. Navigate to your account, then click Workers in the sidebar
  3. Click the blue button to create a service:
Regional Geolocation: 3 Methods To Try 2
  1. Give your Worker a name, choose the HTTP Handler option, and click Create Service:
Regional Geolocation: 3 Methods To Try 3
  1. The Worker has been created, now we need to tell it what to do. Click Quick Edit to start adding code:
Regional Geolocation: 3 Methods To Try 4
  1. Copy-paste the code from the tutorial into the worker, and click Save and Deploy:
Regional Geolocation: 3 Methods To Try 5
addEventListener("fetch", (event) => {
  event.respondWith(addHeaders(event.request))
})

async function addHeaders(request) {
  request = new Request(request)

  if ((cf = request.cf)) {
    request.headers.set("X-Worker-Latitude", cf.latitude)
    request.headers.set("X-Worker-Longitude", cf.longitude)

    request.headers.set("X-Worker-Country", cf.country)
    request.headers.set("X-Worker-Region", cf.regionCode)
    request.headers.set("X-Worker-PostalCode", cf.postalCode)
    request.headers.set("X-Worker-City", cf.city)
  }

  let response = await fetch(request)

  return response
}
  1. Now we need to set up the Triggers, so that the worker knows when to run. Here's how I set up mine (these could be improved by limiting it only to legitimate routes, i.e. not /wp-admin):
Regional Geolocation: 3 Methods To Try 6
  1. Finally, add this snippet and use smoothw_geolocation3() to return the value:
<?php
function smoothw_geolocation3() {
    // geolocation from Cloudflare
	$region = $_SERVER["HTTP_X_WORKER_REGION"]; // get 2-letter state code
	$country = $_SERVER["HTTP_CF_IPCOUNTRY"]; // use this if checking US-based

    // return the one you want, comment out the other
    return $region;
    // return $country;
}
?>

Summary

I've been happy with each of these options at different times, and they all have their benefits. There's a lot of information online about geolocating to the country level, hopefully this helps anyone who like me, needs it to the state level.

As always, if you need this adapted to your personal use case, or you want help with something else, reach out to me at [email protected] for my rates.

crossmenu linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram