Visitor Geolocation
Sentiero can enrich each session with the visitor’s country, city, region, and timezone. Resolution happens server-side at event ingest; the recorder is unchanged and no IP address is ever stored — deliberately coarse, in keeping with anonymize_ip.
Geo capture is off by default. Location headers are ordinary request headers — any client can send them — so only enable a source that your infrastructure actually guarantees.
Cloudflare
If your app is behind Cloudflare, enable IP geolocation (Network settings) for country, and the “Add visitor location headers” managed transform (Rules → Transform rules) for city/region/timezone. Then:
Sentiero.configure do |config|
config.geo_source = :cloudflare
end
Sentiero reads CF-IPCountry, CF-IPCity, CF-Region, and CF-Timezone, skipping Cloudflare’s XX (unknown) and T1 (Tor) markers. Country-only capture (no managed transform) is fine — the other fields just stay unset.
Caveat: only set
:cloudflarewhen requests genuinely arrive through Cloudflare. If clients can reach your origin directly, they can spoof these headers.
Any other source
geo_source also accepts any callable taking the Rack env and returning a Hash with "country", "city", "region", and/or "timezone" string values. This covers other CDNs:
# CloudFront (enable the CloudFront-Viewer-Country header policy)
config.geo_source = ->(env) { {"country" => env["HTTP_CLOUDFRONT_VIEWER_COUNTRY"]} }
or a self-managed MaxMind database — resolve the IP in-request and return only the coarse result, so the IP itself is never persisted and anonymize_ip is unaffected:
# Gemfile: gem "maxmind-geoip2"
GEO_DB = MaxMind::GeoIP2::Reader.new(database: "/srv/geoip/GeoLite2-City.mmdb")
config.geo_source = lambda do |env|
record = GEO_DB.city(env["REMOTE_ADDR"]) # resolve, then discard the IP
{"country" => record.country.iso_code, "city" => record.city&.name}
rescue MaxMind::GeoIP2::AddressNotFoundError
nil
end
A resolver that raises or returns something other than a Hash is ignored (with a one-time warning) — geo problems never break event ingest.
Where it shows up
Resolved values are stored as geo_country, geo_city, geo_region, and geo_timezone in the session’s metadata:
- Analytics overview — Countries and Cities cards (shown once geo data exists).
- Segments — a Country filter dropdown; filter by city with the generic metadata filter (
metadata_key=geo_city). - Session detail — in the session’s metadata panel.
If a recorded page sets the same key via setMetadata(), the page’s value wins.