Point a domain at CloudFront or a load balancer

Route a domain (including the zone apex) to a CloudFront distribution or load balancer with a Route 53 alias record — in the CLI or TypeScript — wait for the change to reach every name server, and work through why a domain still is not reachable (registrar name servers, suspension, deleted hosted zone, caching).

markdownguidev4N92F-GPPWstage: buildtrust: officialservice: route53interface: cliinterface: sdkRoute 53 alias recordzone apexA record aliasAAAA aliasChangeResourceRecordSetsCommandUPSERTGetChange INSYNCCanonicalHostedZoneIdEvaluateTargetHealthCNAME at apex not allowedname servers registrardomain not resolvingdomain suspended ClientHoldhosted zone deleted

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

**Use an alias record, type A (plus AAAA for IPv6), whose target is the resource's DNS name and its hosted zone ID.** Aliases work at the zone apex (example.com), where a CNAME is not allowed. They follow the resource's IP changes, and Route 53 doesn't charge for alias queries to AWS resources.

1. Collect the target

TargetDNSNameAlias HostedZoneId
Application or Network Load Balanceraws elbv2 describe-load-balancers --names <lb> --query 'LoadBalancers[0].DNSName'…[0].CanonicalHostedZoneId
CloudFront distributiond1234abcd.cloudfront.net (aws cloudfront get-distribution --id <id> --query Distribution.DomainName)CloudFront's fixed zone ID (UNVERIFIED value not in the collected sources; the console fills it in)

Before pointing a name at CloudFront, add that name as an alternate domain name on the distribution, with a certificate in us-east-1 that covers it. Otherwise requests return 403. A load balancer needs the name on its listener certificate, or browsers show NET::ERR_CERT_COMMON_NAME_INVALID.

2. Create or update the record

import { Route53Client, ChangeResourceRecordSetsCommand, GetChangeCommand } from "@aws-sdk/client-route-53";

const r53 = new Route53Client({});

const out = await r53.send(new ChangeResourceRecordSetsCommand({
  HostedZoneId: "<your hosted zone id>",
  ChangeBatch: {
    Comment: "apex to load balancer",
    Changes: [{
      Action: "UPSERT",
      ResourceRecordSet: {
        Name: "example.com",
        Type: "A",
        AliasTarget: {
          HostedZoneId: "<the load balancer's CanonicalHostedZoneId>",
          DNSName: "<the load balancer's DNSName>",
          EvaluateTargetHealth: true,
        },
      },
    }],
  },
}));

let status = out.ChangeInfo?.Status;
while (status === "PENDING") {
  await new Promise((r) => setTimeout(r, 5_000));
  status = (await r53.send(new GetChangeCommand({ Id: out.ChangeInfo!.Id! }))).ChangeInfo?.Status;
}
console.log(status); // INSYNC

The npm package is @aws-sdk/client-route-53, with a hyphen. CLI: aws route53 change-resource-record-sets --hosted-zone-id <zone> --change-batch file://change.json with the same JSON, then aws route53 get-change --id <change-id>.

  • Two HostedZoneIds: the request's is your zone; the one inside AliasTarget belongs to the target resource. Swapping them is the most common mistake (UNVERIFIED frequency; the error returned is InvalidChangeBatch).
  • UPSERT creates or replaces; CREATE fails if the record exists; DELETE must repeat every value exactly.
  • A batch is all-or-nothing. Replacing a www CNAME with an alias in one batch (DELETE + CREATE) has no gap.
  • An alias's TTL can't be set: Route 53 uses the target's.
  • EvaluateTargetHealth: true makes Route 53 skip an unhealthy target in failover or weighted setups.

Changes typically reach all Route 53 name servers within 60 seconds (INSYNC). Resolvers that cached the old answer keep it until its TTL runs out.

3. Check

dig +short example.com
dig +short <lb-dns-name>        # should return the same addresses
dig example.com NS +short       # should list this hosted zone's four name servers

An alias shows up in dig as a plain A/AAAA record. Its alias nature is visible only in the Route 53 console or in list-resource-record-sets.

When the domain still doesn't resolve

CauseFix
The registrar's name servers aren't this hosted zone's (common when several zones share the name)Compare the zone's four NS with the registrar's. Update the registrar. Resolvers cache NS for 24–48 h, so it can take up to two days
New registration not confirmed (registrant email link, valid 3–15 days by TLD)The domain is suspended until you confirm. Resend the email
Registration transferred, but the old registrar's DNS service stoppedRecreate the records in a Route 53 hosted zone and set its name servers at the registrar
DNS moved, then registration transferred too soonWait out the NS caching; nothing speeds it up
The hosted zone was deletedCreate a new zone, recreate the records, set the new name servers at the registrar, then wait (up to 48 h)
Suspended (ClientHold)Resolve the registrar's reason, such as an unconfirmed contact

If DNS is right and connections time out, the problem is the network: guides/vpc/diagnose-connection-timeout.md.

Read this document as markdown — the same bytes an agent receives.