> For the complete documentation index, see [llms.txt](https://mercure-technologies.gitbook.io/xprem/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mercure-technologies.gitbook.io/xprem/identity/geolocation.md).

# Geolocation

Geolocation is optional and off by default. When you enable it, every check-in resolves a coarse place for the device (country, city, coordinates), which the Identity section of the dashboard uses to show where your installs are in the world. The place is resolved at request time and only the result is stored: **the registry never persists an IP address**.

There are two strategies. If your traffic goes through a proxy or CDN that already knows where each visitor is, let it stamp that knowledge on the request and trust its headers. Otherwise, give the server MaxMind credentials and it downloads a GeoLite2 database to resolve client IPs locally. If both are configured, the headers strategy wins.

### Strategy 1: trusted proxy or CDN headers

Set `TRUST_GEOIP_HEADERS=true` and the server reads the visitor-location headers your edge already adds. Nothing is downloaded and no external service is called; the work was already done by the proxy. The known vocabularies are recognized automatically, tried in this order:

| Provider   | Headers                                                                                    |
| ---------- | ------------------------------------------------------------------------------------------ |
| Cloudflare | `CF-IPCountry`, `CF-IPCity`, `CF-IPLatitude`, `CF-IPLongitude`                             |
| CloudFront | `CloudFront-Viewer-Country`, `-City`, `-Latitude`, `-Longitude`                            |
| Vercel     | `X-Vercel-IP-Country`, `X-Vercel-IP-City`, `X-Vercel-IP-Latitude`, `X-Vercel-IP-Longitude` |
| Generic    | `X-Geo-Country`, `X-Geo-City`, `X-Geo-Latitude`, `X-Geo-Longitude`                         |
| Akamai     | `X-Akamai-Edgescape` (comma-separated `key=value` pairs)                                   |

Most providers ask you to enable the feature first: on Cloudflare, turn on the "Add visitor location headers" managed transform; on CloudFront, add the `CloudFront-Viewer-*` headers to the origin request policy; on Vercel, the headers are always present. The generic `X-Geo-*` set is the convention for proxies that let you name the headers yourself, such as nginx with its geoip module or a custom edge.

If your proxy uses names the catalog does not know, declare them explicitly; the four variables below replace the whole catalog when set:

| Variable                 | Meaning                                     |
| ------------------------ | ------------------------------------------- |
| `GEOIP_HEADER_COUNTRY`   | Header carrying the two-letter country code |
| `GEOIP_HEADER_CITY`      | Header carrying the city name               |
| `GEOIP_HEADER_LATITUDE`  | Header carrying the latitude                |
| `GEOIP_HEADER_LONGITUDE` | Header carrying the longitude               |

The server drops the "unknown" markers providers use (`XX`, `T1`, `ZZ`) and treats `0,0` coordinates as no location, so a device is either genuinely located or not located at all.

{% hint style="warning" %}
Only enable `TRUST_GEOIP_HEADERS` when the server is reachable exclusively through the proxy that adds the headers. A client that can reach the server directly can forge any location by sending the headers itself.
{% endhint %}

Note that a plain load balancer (AWS ALB, a default Google Cloud load balancer, vanilla nginx) adds no location headers natively. If that is your setup, use the MaxMind strategy instead.

### Strategy 2: a MaxMind GeoLite2 database

With MaxMind credentials, the server downloads the free GeoLite2 City database itself and resolves each client IP locally, so no file has to be mounted and no per-request call leaves your infrastructure. [Sign up for a free MaxMind account](https://www.maxmind.com/en/geolite2/signup), [generate a license key](https://www.maxmind.com/en/accounts/current/license-key), and set both variables (the server refuses to start with only one of them):

| Variable              | Meaning                                                                                                                                                            |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MAXMIND_ACCOUNT_ID`  | Your MaxMind account id                                                                                                                                            |
| `MAXMIND_LICENSE_KEY` | A license key generated in the MaxMind account portal                                                                                                              |
| `GEOIP_CACHE_DIR`     | Optional. Where the downloaded database is cached; defaults to the user cache directory. Point it at a writable volume when the container filesystem is read-only. |

The download happens once at startup: the archive is fetched over HTTPS, verified against its published checksum, and cached on disk, so a restart with an up-to-date cache does not download anything. There is no periodic sync; MaxMind republishes GeoLite2 twice a week and each restart or deploy picks up the newest build, which is more than enough for data that drifts slowly. A failed download never blocks the server: it keeps serving updates and simply leaves devices unlocated until the next restart.

Since this strategy resolves the client IP, make sure the server sees the real one: behind a load balancer, set `TRUST_PROXY_HEADERS=true` so the client is read from `X-Forwarded-For` (see the environment variables reference for `TRUST_PROXY_DEPTH`). Private and loopback addresses are never resolved.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mercure-technologies.gitbook.io/xprem/identity/geolocation.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
