Country Data Brainstorming

Today I'll try and figure out how to collect country and device data.

There seem to be a few viable approaches:

1. On the client

Using the JS api, get the timezone on the client and figure out the country from it.

This one is the easiest and cheapest to implement, but it's not that accurate. Some countries might share the timezone

2. On the server

I could package a free GeoIP database with my backend and match the requests using it. This would work behind a proxy too, as nginx (and probably most other proxies) adds the X-Forwarded-For header.

It does seem a bit icky handling this on the server, but it would work. I would have to ship the database (not more than 10 MB) with the server image and rebuild it every time the database updates.

The most popular MaxMind GeoIP database is not free and pretty expensive for me at this stage. There is a free alternative called IP2Location with only attribution needed. There's also DB-Ip with the same license, but they don't have an nginx module.

3. On the proxy layer

This would include using nginx to match the ips to countries using a GeoIP database.

This has the same issues as the server approach, but comes at a benefit of not having to package the db with the backend.

4. On Cloudflare

This involves enabling proxying on my Cloudflare Domain and enabling the visitor location headers.

After going through all options, this seems like the best one. I'll try implementing it.

alt text

This requires me to update my DNS config Proxy status from DNS Only to Proxied. This means that the requests no longer go directly to my server, but go through the Cloudflare proxy first. This is fine, as I was going to do this anyways sometime in the future for bot protection.

alt text

I added a simple test to my Nginx

  location /debug-cf {
      return 200 "Country: '$http_cf_ipcountry'\nCity: '$http_cf_ipcity'";
      add_header Content-Type text/plain;
  }

and I get the response:

Country: 'HR'
City: 'Zagreb'

It works! Now I need to forward it to the backend. I'll add this to the nginx config.

proxy_set_header CF-IPCountry $http_cf_ipcountry;
proxy_set_header CF-IPCity $http_cf_ipcity;

But there's a problem! My requests aren't going directly from the clients, they're being proxied by NextJS. This will also be a problem for any other proxy that the users might use, and they will have to use it to avoid adblockers.

diagram

I wonder if Vercel is preserving the X-Forwarded-For IP. I'll add some logs.

    "X-Forwarded-For": [
      "86.33.18.111,3.77.234.194"
    ],

Seems like the first one is mine, and the second is Vercel. Is cloudflare using the second one? Or is Cloudflare appending it? I'll check the docs

Seems like it is, judging from the docs:

If, on the other hand, an X-Forwarded-For header was already present in the request to Cloudflare, Cloudflare will append the IP address of the HTTP proxy connecting to Cloudflare to the header.

Could I configure the Cloudflare proxy to use the X-Forwarded-For IP to resolve the country?

Seems like that's not possible. So the only real option here for precise locations is rolling a Geo IP database on the backend, or accept the inaccurate data resolved from the browser timezone.

I'll decide some other time, too tired now.