Cloudflare Workers IP Geolocation: Caching and Cold Starts
Cloudflare Workers IP geolocation means reading location data about a visitor inside a Worker, either from the built-in request.cf object or by calling an external IP intelligence API from the edge. The two approaches answer different questions, and the caching and cold-start behavior of each is worth understanding before you build on it.
What Workers gives you for free
Every request that reaches a Worker carries a request.cf object populated by Cloudflare. It includes the visitor's country, city, region, coordinates, timezone, ASN and AS organization, plus the colo field that names the data center that handled the request. The CF-IPCountry header carries the country code as well. Because this data is attached before your code runs, it costs no extra subscrequest and no extra latency.
The limit is scope. request.cf describes the connecting client of the current request and nothing else. It cannot tell you about an IP address that appears in a log line, a form field, a webhook payload or an X-Forwarded-For chain, and it carries no reputation signals such as VPN, Tor or abuse flags. Once you need to enrich an arbitrary address, you are calling an API, and that is where caching decisions begin.
Caching enrichment data at the edge
An IP lookup from a Worker is a subrequest, so every uncached lookup adds a network round trip to the request path. Cloudflare offers several places to cache the result: the Cache API (per data center, not replicated globally), KV (globally readable but eventually consistent, with writes taking time to propagate), and Durable Objects when you need a single coordinated copy. None of them is a drop-in global cache, so pick by what you can tolerate: the Cache API is simplest and fastest for read-heavy lookups, while KV suits values you want visible from every location.
The more useful design decision is the time-to-live per field, because IP intelligence data does not age uniformly. Country and ASN assignments change slowly, since address blocks are reallocated over months or years, so a TTL of many hours is reasonable. Reputation flags are different. ipto's VPN, Tor and abuse feeds refresh every 6 hours, and its IP and ASN datasets refresh daily, so caching a flag like is_tor for longer than a few hours means you will serve answers older than the source itself. A practical split is to cache the network facts (asn, asn_org, country, city) for around a day and the flags (is_vpn, is_tor, is_abuser, is_datacenter) for no more than the feed interval.
- Key the cache on the normalized IP (for IPv6, consider keying on the /64 if your use case is per-network rather than per-host).
- Cache negative results too, so repeated lookups of an unlisted address do not hit the API every time.
- Never cache bogon results under a public key by accident: a private address like
10.0.0.1is answerable locally without any API call.
Cold starts on Workers
Workers run in V8 isolates rather than containers, so a cold start is typically a matter of milliseconds and not the hundreds of milliseconds familiar from container-based functions. Your code still pays for its own initialization: a large bundle, heavy module-level parsing of a big lookup table, or top-level awaits all happen on the first request to a fresh isolate. The fix is ordinary: keep the bundle small, avoid building big in-memory structures at startup, and prefer a single small API call to shipping an entire geolocation database inside the Worker.
The warm-up pings that people use with traditional serverless are mostly wasted here. Isolates are created and evicted on Cloudflare's schedule, a ping reaches only one location, and the cost of a cold start is small enough that caching the lookup result matters far more than keeping the isolate alive. Measure first with Workers analytics and with your own timing around the subrequest before optimizing anything.
Putting it together with ipto
A sensible pattern is to use request.cf for the instant, free facts about the connecting client and call an enrichment API only when you need more. ipto's API documentation shows the IP lookup response, which returns country, city, ASN, org, currency_code and the flags is_vpn, is_tor, is_abuser, is_datacenter and bogon/bogon_type. It is free with no signup and 1,000 free requests per day, which is enough to prototype a Worker without a key management step.
When the lookup tells you the address belongs to a hosting network, you can follow up in the ASN lookup to see who operates it and which ranges it announces. For flagged traffic, the VPN, Tor and proxy checker shows the evidence behind is_vpn and is_tor, and the IP abuse checker shows the corroborated score. Remember that city-level location is an estimate of where a network is registered or routed, not where a person sits, a limitation explained in why city accuracy is limited.
The edge makes lookups fast, but it does not make the underlying data more precise. Treat request.cf and any API result as the same class of evidence, cache each according to how quickly it changes, and let the freshness of the source, not the speed of the edge, decide your TTLs.