Route 53 can route traffic based on the originating IP address of the request, not just the DNS query itself.

Imagine you have a fleet of servers, some in a private subnet and some in a public one, and you want to direct users to the closest or most appropriate server based on where they’re coming from. Route 53’s IP-based routing lets you do exactly that, bypassing traditional DNS resolution that only cares about the domain name.

Let’s see this in action. We’ll set up a simple scenario: users from within a specific Amazon VPC should hit a private IP address, while everyone else gets a public IP.

First, we need a Route 53 Hosted Zone. Let’s say it’s example.com.

aws route53 list-hosted-zones --query "HostedZones[?Name == 'example.com.']"

Now, we’ll create a health check that monitors our public endpoint. This is crucial because Route 53 needs to know if the endpoint is available.

aws route53 create-health-check --caller-reference `date +%s` \
--health-check-config IPAddress=54.123.45.67,Port=80,Type=HTTP,RequestInterval=30,FailureThreshold=3,SearchString="OK"

Next, we define our public endpoint. This is a standard A record pointing to our public IP.

aws route53 change-resource-record-sets --hosted-zone-id Z123ABCDEF4567 \
--change-batch '{
    "Comment": "Public endpoint for example.com",
    "Changes": [
        {
            "Action": "CREATE",
            "ResourceRecordSet": {
                "Name": "example.com.",
                "Type": "A",
                "TTL": 60,
                "ResourceRecords": [
                    {"Value": "54.123.45.67"}
                ]
            }
        }
    ]
}'

Now for the magic. We’ll create a new record set for example.com but this time, we’ll use the GeolocationRoutingPolicy and specify a CIDR block. This CIDR block will represent our internal VPC IPs. For illustration, let’s use 10.0.0.0/16.

aws route53 change-resource-record-sets --hosted-zone-id Z123ABCDEF4567 \
--change-batch '{
    "Comment": "Internal VPC endpoint for example.com",
    "Changes": [
        {
            "Action": "CREATE",
            "ResourceRecordSet": {
                "Name": "example.com.",
                "Type": "A",
                "TTL": 60,
                "SetIdentifier": "internal-vpc-endpoint",
                "GeolocationRoutingPolicy": {
                    "CidrRoutingConfiguration": {
                        "GeolocationLocation": {
                            "Country": "US"
                        },
                        "EndpointConfiguration": {
                            "Types": ["IPAddress"],
                            "SetIdentifier": "internal-vpc-endpoint",
                            "IPAddress": {
                                "IP": "10.0.1.10",
                                "SubnetMask": "24"
                            }
                        }
                    }
                }
            }
        }
    ]
}'

Wait, that’s not quite right. The GeolocationRoutingPolicy with CidrRoutingConfiguration is for geo-proximity routing based on client IP. What we actually want is to specify a specific CIDR block and point it to an endpoint. This is achieved by using the CIDR routing policy directly on the record set, not within GeolocationRoutingPolicy.

Let’s correct that. We’ll create a record set with a CIDR routing policy. This policy only applies when the DNS resolver is configured to send the client’s IP address in the EDNS0 option of the DNS query.

aws route53 change-resource-record-sets --hosted-zone-id Z123ABCDEF4567 \
--change-batch '{
    "Comment": "CIDR-based routing for internal VPC",
    "Changes": [
        {
            "Action": "CREATE",
            "ResourceRecordSet": {
                "Name": "example.com.",
                "Type": "A",
                "TTL": 60,
                "SetIdentifier": "internal-vpc-only",
                "CidrRoutingPolicy": {
                    "Cidr": "10.0.0.0/16",
                    "EvaluateTargetHealth": true,
                    "HealthCheckId": "YOUR_HEALTH_CHECK_ID_HERE"
                },
                "ResourceRecords": [
                    {"Value": "10.0.1.10"}
                ]
            }
        }
    ]
}'

Here’s the breakdown:

  • Name: example.com. - The domain we’re routing.
  • Type: A - We’re returning an IPv4 address.
  • TTL: 60 - How long resolvers cache this record.
  • SetIdentifier: internal-vpc-only - A unique name for this specific routing configuration.
  • CidrRoutingPolicy: This is the key.
    • Cidr: 10.0.0.0/16 - This defines the IP address range for which this record set will be returned. If the DNS resolver sees a client IP within this range, it will consider this record set.
    • EvaluateTargetHealth: true - Route 53 will check the health of the endpoint associated with this record set.
    • HealthCheckId: YOUR_HEALTH_CHECK_ID_HERE - The ID of the health check we created earlier (or one specifically for the internal endpoint if you had one).
  • ResourceRecords: {"Value": "10.0.1.10"} - The IP address to return when the CIDR matches.

The public endpoint we created earlier (with 54.123.45.67) will act as the default. When a DNS query for example.com comes in, Route 53 checks the resolver’s IP address (often passed via EDNS0). If that IP falls within 10.0.0.0/16, and the associated health check is healthy, it returns 10.0.0.10. Otherwise, it falls back to the next most specific record set, which in this case is the public one.

The crucial piece of understanding here is how the DNS resolver communicates the client’s IP. This isn’t magic. The resolver (e.g., your VPC’s DNS resolver, an on-premises DNS server, or a public DNS resolver like Google’s 8.8.8.8) must be configured to send the client’s IP address to Route 53 using the EDNS0 extension (OPT pseudo-record) in its DNS queries. If the resolver doesn’t send this information, Route 53 cannot perform IP-based routing and will fall back to a default or the next available record set. In AWS, VPC DNS resolvers do send this information by default.

The next thing you’ll want to explore is how to combine CIDR-based routing with other routing policies, like weighted or latency-based routing, for more complex traffic management scenarios.

Want structured learning?

Take the full Route53 course →