AWS Route 53 Routing Policies

By Vishvesh Patel · DevOps EngineerUpdated September 16, 2026

Weighted, latency, failover, geolocation and multivalue routing. What each is for, the Set Identifier rule, and why weighted canaries drift.

Route 53 Routing Policies

A routing policy decides which answer Route 53 returns when several records share a name. That is how one hostname can serve different users differently, by region, by health, or by a weighting you control.

PolicyAnswers based on
SimpleOne record, no logic
WeightedA proportion you set
LatencyLowest measured latency to the client
FailoverHealth of a designated primary
GeolocationWhere the client appears to be
GeoproximityDistance to the resource, with a bias
MultivalueUp to eight healthy records, at random

Set Identifier is mandatory

Any policy other than simple requires a SetIdentifier. A unique label distinguishing records that share a name and type. Omitting it is the first error everyone hits:

InvalidChangeBatch: RRSet of type A with DNS name api.example.com. is not
permitted because a conflicting RRSet exists

Route 53 is telling you it cannot hold two records with the same name and type unless each carries a distinct identifier.

Weighted

Splits traffic by proportion. The workhorse for canaries and blue-green cutovers:

aws route53 change-resource-record-sets --hosted-zone-id "$ZONE" --change-batch '{
  "Changes": [
    {"Action": "UPSERT", "ResourceRecordSet": {
      "Name": "api.example.com", "Type": "A", "TTL": 60,
      "SetIdentifier": "blue", "Weight": 90,
      "ResourceRecords": [{"Value": "203.0.113.10"}]
    }},
    {"Action": "UPSERT", "ResourceRecordSet": {
      "Name": "api.example.com", "Type": "A", "TTL": 60,
      "SetIdentifier": "green", "Weight": 10,
      "ResourceRecords": [{"Value": "203.0.113.20"}]
    }}
  ]
}'

Weights are relative, not percentages. They are divided by the total, so 90 and 10 gives 90%, and so does 900 and 100. Weight 0 removes a record from consideration unless every record is 0, in which case all are eligible. Setting one side to 0 is the clean way to complete a cutover without deleting anything.

Two caveats worth knowing before you trust a 10% canary.

The split is per DNS query, not per request. A client that resolves once and keeps a connection open sends everything to whichever answer it got, so a small sample can skew badly. Keep TTLs low and give the canary time.

Resolvers cache and clients pool. Real distribution converges on the weights over time but is genuinely noisy at short timescales, which is why weighted DNS is a reasonable canary mechanism and a poor traffic-shaping one. If you need precise percentages, do it at the load balancer with weighted target groups.

Failover

An active-passive pair driven by a health check:

{"Action": "UPSERT", "ResourceRecordSet": {
  "Name": "www.example.com", "Type": "A", "TTL": 60,
  "SetIdentifier": "primary", "Failover": "PRIMARY",
  "HealthCheckId": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
  "AliasTarget": {"HostedZoneId": "Z35SXDOTRQ7X7K",
                  "DNSName": "prod-alb-123.eu-west-1.elb.amazonaws.com",
                  "EvaluateTargetHealth": true}
}}
{"Action": "UPSERT", "ResourceRecordSet": {
  "Name": "www.example.com", "Type": "A", "TTL": 60,
  "SetIdentifier": "secondary", "Failover": "SECONDARY",
  "AliasTarget": {"HostedZoneId": "Z3AQBSTGFYJSTF",
                  "DNSName": "s3-website-eu-west-1.amazonaws.com",
                  "EvaluateTargetHealth": false}
}}

The primary must have a health check: either an explicit HealthCheckId or EvaluateTargetHealth on an alias. Without one it is always considered healthy and failover never occurs, which is a configuration that looks complete and does nothing.

The secondary does not need one, and a static S3 maintenance page is a good choice for it: it will be serving precisely when everything else is broken, so it should depend on as little as possible.

Remember the timing from the Route 53 overview: detection is around 90 seconds and cached TTLs add their own delay.

Latency

Routes each client to the region with the lowest measured latency from their resolver:

{"Name": "api.example.com", "Type": "A", "SetIdentifier": "eu-west-1",
 "Region": "eu-west-1", "TTL": 60, "ResourceRecords": [{"Value": "203.0.113.10"}]}

Note Region is an AWS region name, and Route 53 uses its own latency measurements rather than geographic distance, which is usually better, since network paths do not follow maps.

The subtlety: latency is measured to the resolver, not the end user. A client using a distant public resolver can be routed to a distant region. Usually minor, and occasionally the explanation for a puzzling report.

Geolocation

Routes by where the client appears to be, which is about compliance and content, not performance:

{"Name": "www.example.com", "Type": "A", "SetIdentifier": "eu",
 "GeoLocation": {"ContinentCode": "EU"}, "TTL": 300, …}

{"Name": "www.example.com", "Type": "A", "SetIdentifier": "de",
 "GeoLocation": {"CountryCode": "DE"}, "TTL": 300, …}

{"Name": "www.example.com", "Type": "A", "SetIdentifier": "default",
 "GeoLocation": {"CountryCode": "*"}, "TTL": 300, …}

Most specific wins: a German client matches the DE record over the EU one.

Always create a * default record. Without it, a client whose location Route 53 cannot determine receives no answer at all: not a fallback, a NODATA response. This is the most common geolocation mistake and it fails for exactly the users you cannot easily reproduce.

Use geolocation for data residency and licensing. For performance, use latency routing.

Geoproximity

Routes by geographic distance, with a bias that expands or shrinks a resource's catchment area:

{"Name": "api.example.com", "Type": "A", "SetIdentifier": "eu-central",
 "GeoProximityLocation": {"AWSRegion": "eu-central-1", "Bias": 40}, …}

A positive bias pulls in traffic from further away; a negative one pushes it out. That is the feature. It lets you shift load toward a region with spare capacity without changing anything else. It is the only policy that needs traffic flow policies in the console for the visual editor, though the API works directly.

Multivalue answer

Returns up to eight healthy records at random, with health checking that simple routing lacks:

{"Name": "api.example.com", "Type": "A", "TTL": 60,
 "SetIdentifier": "host-1", "MultiValueAnswer": true,
 "HealthCheckId": "…", "ResourceRecords": [{"Value": "203.0.113.10"}]}

The difference from a simple record with multiple values is exactly that health awareness: a simple multi-value record keeps returning a dead address, while multivalue omits records whose health check is failing.

It is not a load balancer. There is no connection distribution or session handling, and clients choose from the list themselves. It suits many equivalent endpoints where a real load balancer is unnecessary.

Choosing

GoalPolicy
Canary or blue-greenWeighted
Disaster recoveryFailover
Fastest response globallyLatency
Data residency or licensingGeolocation
Shift load between regionsGeoproximity
Several equivalent hosts, health-awareMultivalue

Policies can be nested through traffic flow, latency routing to pick a region, then weighted within it for a canary, which is where geoproximity's visual editor earns its place.

Common errors

InvalidChangeBatch: RRSet of type A with DNS name api.example.com. is not
permitted because a conflicting RRSet exists

Missing or duplicate SetIdentifier.

InvalidChangeBatch: Health check must be specified for failover records

The primary needs a health check or EvaluateTargetHealth.

Geolocation returns nothing for some users. No CountryCode: "*" default record.

Weighted split does not match the weights. Caching and connection reuse. Expect convergence over time, not precision.

Quick reference

PolicyRequired fields
WeightedSetIdentifier, Weight
FailoverSetIdentifier, Failover, health check on primary
LatencySetIdentifier, Region
GeolocationSetIdentifier, GeoLocation, plus a * default
GeoproximitySetIdentifier, GeoProximityLocation, Bias
MultivalueSetIdentifier, MultiValueAnswer: true

Key takeaways

  • Every non-simple policy needs a unique SetIdentifier.
  • Weights are relative, not percentages, and weight 0 removes a record cleanly.
  • Weighted DNS splits queries, not requests. Fine for canaries, poor for precise shaping.
  • A failover primary without a health check never fails over.
  • Make the failover secondary as dependency-free as possible: a static S3 page is ideal.
  • Latency is measured to the resolver, not the end user.
  • Geolocation without a * default returns nothing to unrecognised clients.
  • Multivalue differs from simple multi-value records by being health-aware.

Check your understanding

Exercise

Failover that never happens

A failover record set has a PRIMARY pointing at an ALB and a SECONDARY pointing at an S3 maintenance page. The primary ALB is down, but traffic keeps going to it. Both records have SetIdentifiers. What is missing?

Exercise

Users who get no answer at all

Geolocation routing sends EU clients to one region and US clients to another. A small number of users report the domain not resolving at all, rather than resolving to the wrong place. Why?


Frequently Asked Questions

What is the difference between latency-based and geolocation routing?

Latency routing sends a client to whichever AWS region Route 53 has measured as fastest for their resolver, so it optimises performance and can send a European user to a US region if the network path happens to be better. Geolocation routing sends them strictly by where they appear to be, regardless of speed, which is what you need for data residency, licensing or language. Use latency for performance and geolocation for compliance, and note that only geolocation guarantees which region serves a given country.

How do I do a canary or blue-green deployment with Route 53?

Weighted routing. Create two records with the same name and type, distinct SetIdentifier values, and weights in the ratio you want: 95 and 5 to start. Shift the weights gradually and set one to 0 to complete the cutover, which removes it from consideration without deleting anything. Keep TTLs at 60 so changes take effect quickly. Bear in mind that weights apply per DNS query rather than per request, so short observations are noisy; for precise percentages use weighted target groups on an ALB instead.

Why do my weighted records not split traffic in the ratio I set?

Because weighting applies to DNS answers, not requests. A resolver caches the answer for the TTL and serves it to everyone behind it, and a client that resolves once then reuses a connection sends all its traffic to whichever endpoint it got. Distribution converges on the configured ratio over time but is genuinely uneven at short timescales, especially with large corporate resolvers. Lower TTLs help. For accurate traffic splitting, do it at the load balancer.

What does Set Identifier do in Route 53?

It distinguishes records that share the same name and type, which is what makes any non-simple routing policy possible. Route 53 otherwise refuses to store two A records for api.example.com. The value is an arbitrary label, so use something meaningful like blue, green, eu-west-1 or primary. Omitting it produces a "conflicting RRSet exists" error, which is the most common first error when moving from simple to weighted or failover routing.

When should I use multivalue answer instead of simple routing?

When you have several equivalent endpoints and want unhealthy ones removed from the answers. A simple record holding multiple values returns all of them regardless of state, so a dead address keeps being handed out; multivalue attaches a health check per record and omits the failing ones, returning up to eight at random. It is not a load balancer. There is no connection distribution and clients pick from the list themselves, but it is a cheap improvement over round-robin A records.

What is geoproximity routing's bias for?

It expands or contracts the geographic area a resource serves. A positive bias draws traffic from further away, a negative one pushes nearby traffic to another resource, so you can shift load toward a region with spare capacity, or away from one under pressure, without touching the application. That gradual adjustment is the distinguishing feature versus geolocation, which assigns whole countries or continents. Geoproximity is also where the traffic flow visual editor in the console is genuinely useful.

Continue Learning

Explore Related Topics

When it breaks

Related Resources