# Anycast and Geo-Routing — How Requests Reach the Nearest PoP

URL: https://www.devopsness.com/blog/anycast-and-geo-routing-nearest-pop
Author: Kiril Urbonas
Published: 2026-07-11
Updated: 2026-07-11
Categories: Cloud
Tags: Networking, Performance, Cdn, Edge, DNS

Summary: A request leaving a laptop somehow lands on a server 20ms away. Here is what actually decides which point of presence answers.

---
# Anycast and Geo-Routing — How Requests Reach the Nearest PoP

Type a domain, hit enter, and something 20ms away answers. The interesting part is not the response — it is the two or three routing decisions that happened before your packet ever left the building. Most people lump them together as "the CDN figures it out." They are actually separate systems, they disagree with each other, and when latency goes weird it is usually because one of them made a call the other didn't expect.

There are three mechanisms doing the work. Understanding which one is in play changes how you debug.

## Anycast: one IP, many locations

With anycast, the same IP address is announced from dozens or hundreds of locations at once. Every PoP running BGP says "I am 104.16.0.1" to its upstream networks. The internet's routing tables then carry your packet to whichever announcement is *topologically* closest — fewest AS hops, per the routing policy of every network in between.

You can see it. Run a trace to a Cloudflare address from two continents and the same IP terminates in different cities:

```
$ dig +short one.one.one.one
1.1.1.1

$ curl -s https://1.1.1.1/cdn-cgi/trace | grep colo
colo=FRA        # from Frankfurt

# same command from a US host
colo=IAD        # from Ashburn
```

Same IP, different building. No DNS trick involved — the DNS answer is identical everywhere. BGP did the sorting. This is why anycast is the default for DNS resolvers and DDoS scrubbing: attack traffic gets spread across every PoP instead of piling onto one.

The catch: BGP optimizes for hop count and each network's own preferences, not for your latency. A packet can be routed to a "nearby" PoP that sits behind a congested peering link or takes a scenic path through a transit provider. Topologically near is not the same as fast. I have watched a user in Sydney get pinned to a Singapore PoP because the return path through a cheap transit provider added 40ms that a direct peer would have avoided.

## GeoDNS: different answers by resolver location

The older approach is to hand out different IPs depending on where the DNS query came from. The authoritative nameserver looks at the resolver's IP (or, with EDNS Client Subnet, a truncated slice of the actual client subnet), maps it to a region, and returns the address of the closest cluster.

This is what Route 53 geolocation and latency routing policies do. A latency record set looks like this:

```json
{
  "Name": "api.example.com",
  "Type": "A",
  "SetIdentifier": "eu-west-1",
  "Region": "eu-west-1",
  "AliasTarget": {
    "HostedZoneId": "Z32O12XQLNTSW2",
    "DNSName": "eu-alb-1234.eu-west-1.elb.amazonaws.com",
    "EvaluateTargetHealth": true
  }
}
```

Add one of these per region and Route 53 answers each resolver with whichever region has the lowest measured latency from that resolver's network. Geolocation policy is the blunt cousin: it routes by continent/country regardless of measured performance, which is what you want for data-residency or licensing rules, not for speed.

## Latency-based routing

Latency routing is GeoDNS with a feedback loop. Instead of a static country-to-region map, the provider keeps a live latency table between networks and your regions and picks the fastest. Route 53 builds this from its own measurements; Cloudflare Load Balancing does it per-pool with steering:

```
# Cloudflare LB pool steering
steering_policy: "dynamic_latency"
default_pools:
  - us-east-pool
  - eu-west-pool
session_affinity: "none"
adaptive_routing:
  failover_across_pools: true
```

`dynamic_latency` steers each request to the pool with the lowest observed RTT and fails over automatically when a pool's health checks trip.

## The gotchas that bite

**Resolver location is not user location.** GeoDNS sees the resolver, not the person. Someone in Denver using 8.8.8.8 may look like they are wherever Google's nearest resolver PoP is. EDNS Client Subnet fixes most of this by passing a client subnet prefix, but not every resolver sends it, and some strip it for privacy.

**DNS TTLs cache your decisions.** If you set a 3600s TTL and then a region goes down, resolvers keep handing out the dead IP for up to an hour. Latency and failover routing want short TTLs (30–60s). That is more query load, which is the price of fast reconvergence.

**Anycast can flap.** When BGP announcements shift — a peering session bounces, a route gets withdrawn — mid-connection traffic can suddenly land on a different PoP. For stateless requests, fine. For anything holding TCP state without a shared session layer, that is a reset.

## Measuring it for real

Synthetic checks from a monitoring vendor's clean, well-peered datacenters will tell you everything is 15ms and lie to you. Real User Monitoring (RUM) — a small script beaconing actual `PerformanceNavigationTiming` and connection RTT from real browsers — is the only thing that shows the Sydney-via-Singapore path. Put the two side by side. When synthetic says fast and RUM says slow, you are looking at a peering or ECS problem, not a code problem.

Smart placement is the payoff. Routing users to the nearest PoP means little if that PoP then makes three round trips to a database on the other side of the planet. Running compute near the *data* — or replicating the data near the compute — is where [edge functions](/blog/edge-computing-playbook-what-to-run-at-the-edge) earn their place. Pair this with [multi-region failover](/blog/multi-region-failover-route-53-health-checks) so a fast path that dies fails over before users notice.

## The call we'd make

Use anycast for anything stateless and attack-exposed — DNS, static edge, TLS termination. Use latency-based DNS routing for origin selection, with short TTLs and health checks wired in. Turn on EDNS Client Subnet if your provider supports it. And do not trust a single synthetic dashboard — put RUM next to it, because the only latency that matters is the one your actual users are paying.

