Get a visitor's approximate country with Cloudflare Workers

Read Cloudflare's country metadata at the edge without exposing the visitor's IP address or setting a tracking cookie.

Country can be a useful hint for choosing an initial currency or region. It is not a reliable way to establish someone's nationality, address or legal rights. VPNs, mobile networks and missing geolocation data all make certainty a bad assumption.

Read the edge metadata, not the trace endpoint

In a Cloudflare Worker, use request.cf.country. This small endpoint returns only a country hint, keeps the visitor's IP out of the response and creates no cookie:

export default {
  fetch(request) {
    const url = new URL(request.url);
    if (url.pathname !== '/api/country/') {
      return new Response('Not found', { status: 404 });
    }
    if (request.method !== 'GET') {
      return new Response('Method not allowed', {
        status: 405, headers: { Allow: 'GET' },
      });
    }

    const value = request.cf?.country;
    const country = typeof value === 'string'
      && /^[A-Z]{2}$/.test(value) && !['XX', 'T1'].includes(value)
      ? value : null;

    return Response.json({ country }, {
      headers: {
        'Cache-Control': 'private, no-store',
        'X-Content-Type-Options': 'nosniff',
      },
    });
  },
};

This is a standalone Worker example. When adding it to an existing site, integrate the exact route into that site's router rather than replacing the site's entrypoint. Local requests may not contain cf metadata, so null is an expected result.

Let the visitor choose

Use the hint to suggest a region, then provide an obvious override. Do not cache one visitor's country response for everyone. If the server can make the display decision itself, skip the extra browser request entirely.

There is no need to expose /cdn-cgi/trace data to application code or store an IP address just to offer a country choice. Do not use this snippet to decide whether a visitor deserves privacy controls; design those controls explicitly for your service.

Reference: Cloudflare's incoming request metadata.

Original version5 November 2020

Kept here for reference and earlier links. The updated guide above is the recommended starting point; older code may depend on retired services or different software versions.

Are you looking to have data on your users country they're visiting from, IP (or more)? Then the below solution will allow just that, though only if your site is using Cloudflare.

After lots of trial and error I found a solution to get the users country, as well as IP and further info and store it in a cookie.

This is particularly useful if you are implementing cookie banners on your site, as only certain countries require these banners to be shown.

Pro's:

  • Very fast
  • Unlimited lookups

Prerequisite's / Con's:

Your site must be using:

  • CloudFlare
  • jQuery

The JavaScript:


function parseCFTrace(url) {
  let trace = [];
  jQuery.ajax(url, {
    success: function (response) {
      let lines = response.split("\n");
      let keyValue;
      lines.forEach(function (line) {
        keyValue = line.split("=");
        trace[keyValue[0]] = decodeURIComponent(keyValue[1] || "");

        if (keyValue[0] === "loc" && trace["loc"] !== "XX") {
          var date = new Date();
          date.setTime(date.getTime() + 365 * 24 * 60 * 60 * 1000);
          var expires = "; expires=" + date.toGMTString();
          document.cookie =
            "countryCookie" + "=" + trace["loc"] + expires + "; path=/";
        }
      });

      return trace;
    },
    error: function () {
      return trace;
    },
  });
}

let CFcountryLookup = parseCFTrace("/cdn-cgi/trace");

Note:

You'll probably want to add some kind of if/else check to see if the users cookie has already been set, we don't need to check their country and set the cookie one very visit. I suggest doing this with PHP before echo'ing out the above JS code.

How it works

This piece of javascript will dynamically make a call to the Cloudflare IP and retrieve some information about the users visit, in total we have access to the following dataset, most notably/useful are "ip" and "loc" (location).
We then set a cookie called "countryCookie" with the ISO 2 letter version of their country, that will last for 1 year.


0: "fl"
1: "h"
2: "ip"
3: "ts"
4: "visit_scheme"
5: "uag"
6: "colo"
7: "http"
8: "loc"
9: "tls"
10: "sni"
11: "warp"
12: "gateway"

Keep exploring

A couple more useful notes.