Geographic Traffic Heatmap Analysis: Visualizing CDN Usage by Region

Last winter a video customer called me at 2 a.m. because their origin was melting down, but our global dashboard showed everything as green. The problem wasn't global at all — it was hyper-regional. Nearly 40% of their evening traffic was coming from just two metro areas in Brazil where a single PoP was saturated, and we couldn't see it until we mapped the logs. That's the moment I always point to when someone asks why a geographic traffic heatmap matters more than a line chart.

Why Raw Logs Won't Show You Where Traffic Really Lives

When you're starting out with CDN analytics, it's tempting to stare at total requests per minute and call it a day. I've done it too. Early in my career I spent weeks tuning cache rules based on global hit ratios, only to realize we were optimizing for North America while ignoring that 60% of our cache misses were clustered in Southeast Asia. Raw logs give you volume, but they don't give you shape. Without geographic analysis, you can't tell if your traffic is evenly spread or piled up in a handful of regions that are about to fall over.

A traffic heatmap fixes that by turning millions of log lines into a visual story. Think of it as a weather map for your CDN. Instead of showing temperature, it shows request density, bandwidth, or error rates by geography — country, state, or even city if you have the data. For beginners, the key insight is this: regional traffic isn't just a reporting nicety, it's how you find capacity problems, bad routing, and surprising growth before your users complain. I once spotted a 10x traffic spike in Poland that turned out to be a partner's app update gone wrong — we saw it on the heatmap hours before alerts fired.

To create that view you need three things from your logs: a timestamp, a geographic indicator, and a metric to visualize. Most CDNs log the edge PoP location and the client IP, and you'll enrich that IP with GeoIP data later. If you're still building your logging pipeline, you'll want to understand how those events flow first. Our setup relies on CDN Real-Time Log Streaming Architecture: From Edge Events to Analytics in Seconds to get edge events into our warehouse without delay, and the concepts in CDN Log Aggregation Architecture: Collecting Billions of Events Without Dropping Data are essential reading before you start aggregating by region — you don't want to lose the very spikes you're trying to visualize.

# Minimal example: parsing CDN access logs to count requests per country
# Log format: timestamp, client_ip, edge_pop, status, bytes
import geoip2.database
import collections

reader = geoip2.database.Reader('GeoLite2-Country.mmdb')
requests_by_country = collections.Counter()

with open('/var/log/cdn/access.log') as f:
    for line in f:
        parts = line.strip().split()
        client_ip = parts[1]
        try:
            country = reader.country(client_ip).country.iso_code or 'UNKNOWN'
        except:
            country = 'UNKNOWN'
        requests_by_country[country] += 1

# Result ready for heatmap: {'US': 482103, 'BR': 210492, 'IN': 198332, ...}
for country, count in requests_by_country.most_common(10):
    print(f"{country}: {count}")

Turning Edge Logs Into a Clean Dataset for CDN Visualization

Getting from raw logs to a usable dataset for CDN visualization is where most beginners get stuck, and I get it — the data is messy. Client IPs don't equal user location, bots inflate counts, and sampling can hide the details you need. The first thing I tell my team is: don't try to map every single request. Aggregate early, clean as you go. We typically roll up logs into 1-minute or 5-minute buckets per region. That keeps the dataset small enough to query quickly but detailed enough to show real patterns. If you're dealing with huge volumes, read up on CDN Log Sampling Strategies: Reducing Storage Costs Without Losing Signal before you decide to drop data — aggressive sampling will wash out the small regional hotspots you're trying to find.

Your enrichment step is critical. We use MaxMind GeoIP2 at the log processing stage to add country, region, and city to each event, but you can also do it later in your warehouse with a SQL join. You'll also want to filter out health checks and synthetic monitoring traffic, otherwise your heatmap will glow in Ashburn, Virginia where all your probes originate. I learned that the hard way when we spent a day investigating "traffic" from Virginia that was just our own monitors. Also, remember that the geographic fields you add should respect privacy and standards — the HTTP Working Group Specifications are a good reference for what headers like X-Forwarded-For actually mean, and the web.dev Articles have solid guidance on handling GeoIP with performance and user privacy in mind.

Once your data is clean, the visualization itself is surprisingly simple. You don't need a fancy GIS platform to start. A choropleth map where color intensity equals requests per second per region is enough to answer 80% of questions. I usually start with country-level, then drill to state or city only when a country lights up. The query below is similar to what we run in ClickHouse every five minutes to feed our heatmap dashboard. It gives us a table of regional traffic that's ready to plot.

-- Aggregate CDN logs for heatmap visualization (ClickHouse / BigQuery style)
SELECT
  geo_country_code,
  geo_region,
  count() AS request_count,
  sum(bytes_sent) / 1024 / 1024 AS mb_served,
  avg(cache_hit = 1) AS hit_ratio,
  quantile(0.95)(request_time) AS p95_latency_ms
FROM cdn_access_logs
WHERE timestamp >= now() - INTERVAL 1 HOUR
  AND user_agent NOT LIKE '%HealthChecker%'
  AND status BETWEEN 200 AND 399
GROUP BY geo_country_code, geo_region
HAVING request_count > 1000  -- filter noise
ORDER BY request_count DESC
LIMIT 100;

How to Read Regional Traffic Patterns Without Guessing

A heatmap is only useful if you know what the colors are trying to tell you. New folks often see a dark red region and assume "bad," but context is everything. High traffic density in a region with a high cache hit ratio and low latency is actually a good sign — it means your PoP there is doing its job. High density with a low hit ratio and high latency is a flashing warning that you're either short on capacity or your cache keys are fragmented. I always teach engineers to look at three layers together: volume, efficiency (hit ratio), and experience (latency or error rate). When those three line up on the map, the cause becomes pretty obvious.

Here's how we triage when we look at a geographic heatmap in the morning. First, we scan for unexpected hotspots compared to the same time last week — is a region that was light blue now dark red? Second, we cross-check if that hotspot correlates with performance. If latency in that region jumped, we check PoP health and origin routing. If latency is flat, it might just be a successful marketing launch. That distinction saves a lot of false alarms. You don't want to wake up an ops team because a soccer final drove legitimate traffic to Germany; you do want to wake them if error rates spiked there at the same time.

The table below is a real example from one of our entertainment customers during a live event window — a 5-minute snapshot that our heatmap summarized. You can see why the visualization matters: the US-East region had the highest volume but wasn't the problem. Brazil Southeast had half the requests but nearly triple the bandwidth per request and a collapsing hit ratio, which told us large video segments weren't caching properly at the São Paulo PoP.

Region Requests / min Bandwidth (Gbps) Cache Hit Ratio p95 Latency (ms)
US-East (Virginia) 182,400 4.2 0.94 68
Brazil Southeast (São Paulo) 89,700 11.8 0.61 244
Germany (Frankfurt) 67,200 3.1 0.91 72
India West (Mumbai) 54,850 6.4 0.88 118
Japan East (Tokyo) 41,300 2.9 0.93 65

Building the Heatmap Pipeline: From GeoIP to Rendered Tiles

We've got clean, deduplicated logs with accurate GeoIP — now we need to turn that 50-million-row-a-day firehose into something your eyes can actually parse. Don't try to plot raw points on a map. You'll end up with a brown blob over Ashburn and Singapore that tells you nothing. The trick is server-side aggregation before you ever touch a visualization library. I typically bucket by H3 hexagon (resolution 5 is my sweet spot ~252 sq km) or by ISO 3166-2 for country-level exec views, then roll up by 5-minute windows. At our peak, that drops a 4.2 TB daily log set to a ~180 MB aggregated table you can query in under 400ms.

Most teams overcomplicate the stack here. You don't need a GIS server. A ClickHouse or BigQuery materialized view feeding a simple Leaflet + D3 front end will handle 99% of use cases. We run GeoIP enrichment at the log shipper level using MaxMind GeoIP2-City with weekly database updates — doing it downstream in your warehouse adds 6-12 hours of latency you'll regret when you're chasing a live event spike. Remember to normalize by population or you'll just be mapping where people live, not where your CDN is hot. A per-capita requests-per-10k metric instantly surfaces that weird 3.8x traffic density we saw in São Paulo's financial district that raw counts completely hid.

If you're already doing Clickstream Analysis at CDN Edge: Understanding User Navigation Patterns, you can reuse that same enriched stream — just swap the session_id aggregation for geo_hash. Here's the aggregation pattern we've run in production for 14 months without touching it:

-- Aggregate CDN edge logs to H3 hexes for heatmap rendering
-- Expects table cdn_logs_enriched (timestamp, client_ip, geo_lat, geo_lon, cache_status, bytes)
WITH filtered AS (
  SELECT geo_lat, geo_lon, cache_status, bytes
  FROM cdn_logs_enriched
  WHERE timestamp >= now() - INTERVAL 1 HOUR
    AND geo_lat IS NOT NULL
    AND BotScore < 30  -- drop datacenter/VPN bots
)
SELECT
  h3LatLngToCell(geo_lat, geo_lon, 5) AS hex_id,
  h3CellToLatLng(hex_id).0 AS lat,
  h3CellToLatLng(hex_id).1 AS lon,
  count() AS req_count,
  sum(bytes) / 1e9 AS gbps_volume,
  avgIf(cache_status = 'HIT', 1, 0) * 100 AS hit_ratio
FROM filtered
GROUP BY hex_id
HAVING req_count > 500  -- cuts noise from single-request hexes
ORDER BY req_count DESC
LIMIT 10000;

When Your Heatmap Lies: Anycast, VPNs, and Population Bias

Your heatmap will lie to you the first three times you build it. I guarantee it. The biggest liar is Anycast misattribution. If you're logging the PoP's location instead of the client's GeoIP, your map will show insane density in Frankfurt, Ashburn, and Tokyo even when your users are actually in Lagos and Lima. We've seen junior teams report a "95% traffic surge in Luxembourg" that was just Cloudflare's or Akamai's PoP IP being geocoded. Always geocode the true client IP from X-Forwarded-For or True-Client-IP after stripping trusted proxies — never the edge socket IP. A quick validation: if more than 18% of your traffic maps to a single city where you *know* you don't have that many users, you've got an Anycast leak.

The second gotcha is VPN and datacenter traffic masquerading as regional demand. On one e-commerce property, 22% of our "German" traffic was actually from Hetzner and AWS Frankfurt exit nodes scraping product pages. It painted the whole Hesse region bright red until we filtered by ASN type and connection speed. I now flag any hex where >40% of requests share the same /24 and have a <15ms TTFB variance — that's not humans, that's a bot farm. Cross-reference your heatmap with User Journey Analytics from CDN Logs: Mapping the Path to Conversion — if a hot spot shows 10k requests but zero session continuity or cart events, don't scale PoPs there, block it.

Finally, don't forget to normalize before you make capacity decisions. A raw heatmap will always tell you to add capacity in New York and London. A per-capita, per-Mbps-availability map tells you the real story: that 2.1M requests from Nairobi on a 3G-heavy network are actually 4x more expensive to serve and 9x more likely to revalidate. That's where adjusting TTLs from 1 hour to 24 hours saved us 31% origin offload. And when you tie regional density back to revenue, as we do with Conversion Attribution from CDN Edge: First-Party Tracking Without Client-Side Scripts, you'll often find your hottest regions aren't your most valuable — we shifted 40% of our image optimization budget from North America to Southeast Asia after the heatmap showed high volume but low conversion, and lifted edge conversion rate by 11.4% in that region.

Frequently Asked Questions

How often should I refresh GeoIP data for accurate heatmaps?

Weekly at minimum, daily if you can automate it. MaxMind and IPInfo reassign about 0.8-1.2% of IPv4 blocks every week. We've measured a 4-7% drift in city-level accuracy after just 30 days without an update, which is enough to shift a suburban hotspot into the wrong metro area. Automate the download in your log shipper and reload without restart.

Should I use cache HITs, all requests, or origin misses for the heatmap?

Plot both, but never mix them in one layer. HITs show where your users are; MISSes + PASS show where your origin is hurting. We render HIT density as the base heatmap and overlay MISS clusters as red dots. One client discovered 68% of their misses were clustered within 300km of a single PoP that had a 41% hit ratio vs. 92% globally — turned out to be a cache-key fragmentation bug, not a traffic problem.

What's the best way to handle low-traffic regions without washing out the map?

Use logarithmic scaling and minimum thresholds. Linear color scales make a region with 500 requests invisible next to one with 500,000. We use log10(req_count) for color intensity and hide any hex with fewer than 500 requests per hour or 1,000 per day, depending on zoom level. This cuts 70% of visual noise while keeping genuine emerging markets visible.

Can I build this with open-source tools instead of Datadog or New Relic?

Absolutely — that's what most of us do at scale. Vector or Fluent Bit -> ClickHouse -> Grafana with the Orchestra Cities Map panel or a custom Leaflet app is our standard for any property doing over 1B requests/month. It costs about $180/month in compute versus $3k+ for a SaaS observability heatmap, and queries 10x faster because you're not fighting their generic data model.

Related Articles

References & Standards: HTTP Working Group Specifications · web.dev Articles · IETF Datatracker