A cookieless tool can still tell you that 40 percent of your pageviews came from one country. The location comes from the network edge at request time, and the address itself never has to be written down. Here is how that works, how far to trust it, and where to stop.
Country and city reporting does not require storing an IP address. The network edge that receives the request already resolves a location from the connecting address. A privacy-first tool keeps the resolved fields (country, region, city) and discards the address.
cf object) and stores country, region, city, timezone, continent, an EU flag and the network's name. There is no IP column in the events table.A browser never volunteers its city. The only thing a website reliably learns about where a visitor is comes from the network layer: the public IP address of the connection. Geolocation is a database lookup that maps address ranges to places. Someone, somewhere, owns that database and ships updates to it.
Classic web analytics ran that lookup on the site owner's own server, which meant the owner's server received and usually logged the IP. A CDN or edge network changes the arrangement. The edge terminates the connection first, resolves the location itself, and hands your code the answer as request metadata. Your code can then use the answer without keeping the input.
That is the model GhostMetrics uses. The tracker (gm.js) sends a small beacon with the page path and a few browser details. It does not send a location. When the beacon reaches the Worker running on Cloudflare, the Worker reads the location Cloudflare attached to the request and builds a record from these fields: country, city, region, timezone, continent, an is-EU flag, the network's autonomous system number and the network's organization name. Then it writes the row.
Two things are worth noticing. First, the browser contributes nothing, so there is no permission prompt and no Geolocation API call; the page never asks for GPS. Second, the timezone field is the one Cloudflare derives for the connection, not the visitor's device clock setting.
For a metric that touches privacy, "we don't store IPs" should be checkable against the schema, so here is the actual shape of one stored pageview row, as far as location goes.
| Stored per pageview | Example shape | What it is |
|---|---|---|
country | Two-letter code such as US or DE | Country from the edge lookup |
region and city | State or province name, city name | Edge estimate; city is blank when the edge returns none |
timezone, continent | IANA zone name, continent code | Also from the edge lookup |
is_eu | 1 or 0 | Whether the edge says the country is in the EU |
asn and as_org | Network number and the network operator's name | Who runs the connection (a carrier, a company, a cloud provider), not who is on it |
session_hash | 16 hex characters | Salted hash that rotates every UTC day; used for grouping, not a location |
What is absent: there is no column for the IP address or the user agent string. Both are used in memory to compute the daily-rotating hash, then dropped. We covered the hash mechanics in unique visitors without cookies. The export (CSV or JSON, every plan) is built from the table's own columns, so what you can download is exactly what is stored; if you do not see an IP column there, it is because none exists.
Honest caveat on scope: this describes what the events table holds. The request still arrives with an IP like any HTTP request, and the Worker reads it to compute the hash. The next section is about why that distinction matters.
This is not legal advice, and no analytics tool, ours included, makes a site compliant by itself. What follows is the reasoning that most privacy reviews follow.
IP addresses are generally treated as personal data. EU regulators and courts have repeatedly taken that view, because an address can often be linked to a subscriber by whoever runs the network. That is the root of the objection to tools that log full addresses. The thing a reviewer asks is: what do you keep, for how long, and can it single out a person?
Coarse, resolved location is a different category of data from a stored address. A country code attached to a pageview does not point at anyone. A city name is coarser than an address but can still narrow things down in a small town, which is why the small-count rule below matters. Storing city and region is a judgment call for your privacy review, not a free pass.
Transient processing still counts as processing. The address reaches the edge and the Worker for the length of the request. A tool that says "we never see IPs" is wrong; one that says "we never store them" can be accurate. Check the exact wording in any vendor's privacy documentation.
Where Google Analytics fits. GA4 also derives location from the IP address; how Google handles and retains that address is set out in Google's own privacy documentation, so check its current docs rather than relying on a summary. The consent question around GA4 is mostly about cookies and cross-context identifiers, not about the location field. We walk through that in is Google Analytics GDPR compliant. For the broader no-banner argument, see web analytics without a consent banner.
The practical position: location-only reporting with no stored IP, no cookie and no cross-day identifier is the lighter end of the spectrum, and GhostMetrics also sends nothing at all when a browser signals Global Privacy Control or Do Not Track. Whether that is sufficient for your jurisdiction and your own legal advice is your call.
Treat every number on a geography panel as an estimate with a confidence gradient. We do not quote an accuracy percentage because we have none from a source we can stand behind, and any figure you see in marketing copy for "city-level accuracy" deserves the same suspicion. The patterns below are structural, not statistical.
Address ranges are allocated to national registries and carriers, so country is the most dependable field. It fails mainly when traffic is deliberately relocated (a VPN) or when a carrier or company routes everything out through another country.
City comes from the database's best guess for the address block. For some ranges that guess lands on the carrier's regional gateway or the provider's head office rather than where the person sits. Two visitors on opposite sides of a metro can show the same city, and one visitor can show a city far from where they sit.
A fifth limit is the database itself: address ranges get reassigned and lookups lag behind. That applies to every provider that uses IP geolocation.
Country-level conclusions (which markets, which languages) hold up well enough to act on once the counts are meaningful. City-level conclusions are suitable for "where are our readers roughly clustered" and not for "we have 12 readers in this suburb". Do not build a regional sales plan on one week of city rows.
In GhostMetrics the Geography view is the Geo tab, with three panels. All of them respect the period selector (24 hours, 7, 30 or 90 days).
The top 20 countries by number of pageviews, shown as two-letter codes (US, DE, BR) with a bar. Two details are easy to miss. The count is pageview rows, not people. And the percentage next to each bar is its share of the rows shown in that list (the top 20), not of all your traffic, so a long tail beyond twentieth place is not in the denominator. Pageviews with no resolved country are left out of this list.
The top 20 city, region and country combinations by pageviews. Behind the scenes the rows are separated by city, region and country, but the panel prints only the city name. If your audience includes both Portland, Oregon and Portland, Maine, you will see two bars with the same label. Check your own geography before assuming a duplicate is a bug.
A donut that splits pageviews by the EU flag. The flag is whatever the edge reported for the country. One quirk: rows with no resolved country default to non-EU, so a sudden rise in the non-EU slice can be a lookup gap and not a change in audience. If you rely on this split, compare it against Top Countries first.
The Live tab lists the country of each currently active session (up to 30 hashes seen in the last five minutes), and clicking a day in the daily chart opens a drill-down with that day's top countries by hits. Both use the same stored field; both inherit the same accuracy limits.
Fold it into the routine in how to read your analytics dashboard weekly: geography once a week, against last week.
Location answers "where". The Who's Looking tab answers a different question: "which network". Every pageview carries the name of the network operator that Cloudflare resolved for the connection, and the tab groups visits by that name. When the network is a company, a university or a cloud provider, you can see the organization's name as a row, and nothing more. There is no list of people, no names and no email addresses.
On most sites a large share of traffic comes from home and mobile connections, where the network name is the access provider, and a row reading a national carrier means a person on their phone, not a company visit. So GhostMetrics classifies each network as residential or not, and by default hides the residential ones. The panel carries an "Excludes residential ISPs" badge for this.
The classification is two lists, and a network is residential if it matches either: a list of named consumer providers across several regions (including big carriers), and a pattern list of generic marker words such as broadband, residential, subscribers and fibre to the home, which catches regional providers nobody would enumerate. The badge is also a button. Click it to show all networks, with each row labelled Residential or Business, so you can audit what was hidden. Nothing is deleted; the filter classifies rows after the query.
Business means only "did not match the residential lists". It does not verify a company. Hosting and cloud providers are not on the residential lists, so a bot, a VPN exit or a link-preview service on a cloud network can show up as a Business row. Read an unfamiliar name as a question, not an answer, and cross-check it against the pages it visited and the referrer.
The Visits column counts stored pageview rows from that network over the selected period. In your own dashboard the table holds the top 50 networks of any kind, and the default view hides the residential ones among them, so on a mostly residential site the business list can be short. Public dashboards (Pro) show the top 50 non-residential networks, with no toggle for the viewer. Use it to answer things like: is a partner's office reading the docs; is a known customer's network on the pricing page; are the visits from a hosting company a sign of automated traffic (see cookieless tracking explained for what a script-based tracker sees).
Location in a cookieless tool is useful because it is coarse and aggregate. Every misuse below turns it into something it was built not to be.
Then act at the level the data supports: write in the language the top countries read, schedule launches around their time zones, point paid campaigns at proven countries, and put your hosting near your largest cluster.
Most privacy-first tools resolve geography from the IP at request time and keep only the result. The differences are in granularity, the source database and where the lookup runs. Rows state qualitative facts; anything numeric is for the vendor's docs.
| Tool | Location reported | Source of the lookup | Notes |
|---|---|---|---|
| GhostMetrics | Country, city (with region), EU split, network organization | Cloudflare edge request metadata | No IP column stored; residential ISPs hidden by default in the organization view; hosted only |
| Plausible | Countries, regions and cities in its dashboard | IP-based geolocation database; check current docs for the source and update schedule | Open source, cookieless; EU-hosted; see our Plausible alternatives |
| Umami | Country, with finer detail depending on the setup | IP-based lookup on the server you run; check current docs for the database it uses | Open source, self-hostable; you run the lookup data yourself; see Umami alternatives |
| Cloudflare Web Analytics | Countries in its reports | Cloudflare's own edge | Free, cookieless; check current docs for anything finer than country; see Cloudflare Web Analytics alternatives |
| GA4 | Country, region, city and more | IP lookup done by Google | Cookie-based consent questions apply to the rest of the product; IP handling is described in Google's privacy docs |
For most small sites the deciding factor is not geography at all. If you must run the lookup on your own infrastructure, or need an open-source codebase to audit, Umami and Plausible fit better than a hosted, closed tool. If you want city and network detail with no server to run, GhostMetrics fits. If you want only a country list for free, Cloudflare Web Analytics is enough.
GhostMetrics reports Top Countries, Top Cities, EU vs Non-EU and a Who's Looking organization view on every plan, from one script tag, with no cookie, no localStorage and no IP address stored. It sends nothing when a browser signals Global Privacy Control or Do Not Track. The free plan is free forever: one site, unlimited pageviews, every dashboard view, full history.
Need more than one site, or a public dashboard URL to share? Pro is $9.99/mo flat with unlimited websites (fair-use ceiling of 250) and a 30-day free trial: a card starts the trial, nothing is charged for 30 days, cancel anytime. Raw export as CSV or JSON is on every plan, including free.
Start free, no card See the live demoDisclosure: GhostMetrics is built by Null Agency and we use it on this site, so we are not a neutral party about our own product. This page is not legal advice. Third-party products, databases and privacy documentation change; check each vendor's current docs before deciding.