Dwight Watson's blog

Geolocate requests with Cloudflare workers

April 27, 2021

A number of apps I've worked on have used HTTP request geolocation to show content that's relevant to the user. Not in a creepy way (where it's used to track and record you), but to easily show search results that are immediately helpful. A simple example is High School Tutors - it's homepage will show the available tutors near your closest city. It's a great way of showing relevant content right out of the box.

Previously I've done this using MaxMind's GeoLite2 database - you can plug in an IP address and it will give you a rough approximation of location with city/suburb names and latitude/longitude. These aren't exact by any means - they don't point to your house - but as long as you've got a point in rough proximity it's good enough. With Laravel GeoIP is an easy plug-and-play package that can download this database on deploy and handle the lookup for you.

However, I wanted to explore another option using Cloudflare Workers. This would alleviate my need to download this database and maintain that setup in my app, and instead offload that functionality. Luckily Cloudflare Workers can easily expose a ton of relevant information as well. With 100k free requests per day and plans starting from $5/month after that it's a really nice option to get going.

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
}

This worker will simply add headers to the incoming request - X-Worker-Latitude and X-Worker-Longitude will give you the approximate latitude and longitude as expected.. Add the worker and then apply it to your site in Cloudflare (you can apply it to every request, or only those matching certain requests, if that helps). I find that this generally runs with an overhead of 0.9ms perforamnce negligible.

It's worth noting however that this geolocation it isn't quite as accurate as using a proper geolocation database. The location given is more general, so if more accuracy is important to you this may not be a good trade-off. Otherwise this is a really easy solution to get up and running.

A blog about Laravel & Rails by Dwight Watson;
developer of Roomies.com, myRent.co.nz & StudentVIP.com.au.

Follow me on Twitter, or GitHub.