Custom Domain Names

In this tutorial, we’re going to create a REST API and associate our own custom domain with this REST API. This allows us to not use the auto-generated domain name that API Gateway automatically creates when we deploy our REST API.

Installation and Configuration

If you haven’t already setup and configured Chalice, see the Quickstart for a step by step guide. You can run these commands to create a basic Chalice app:

$ python3 --version
Python 3.7.3
$ python3 -m venv venv37
$ . venv37/bin/activate
$ python3 -m pip install chalice
$ chalice new-project customdomain
$ cd customdomain

Configure and Deploy a REGIONAL Endpoint

Before we configure a custom domain for our REST API, we’ll deploy our REST API so we can see the auto-generated domain name that API Gateway creates for us.

First, we’ll change our endpoint type from EDGE (the default) to REGIONAL. Update your .chalice/config.json file so it looks like this:

$ cat .chalice/config.json
{
  "version": "2.0",
  "app_name": "customdomain",
  "api_gateway_endpoint_type": "REGIONAL",
  "stages": {
    "dev": {
      "api_gateway_stage": "api"
    }
  }
}

Now we’ll deploy our application. Note the URL that’s printed when your application is deployed.

$ chalice deploy
Creating deployment package.
Creating IAM role: customdomain-dev
Creating lambda function: customdomain-dev
Creating Rest API
Resources deployed:
  - Lambda ARN: arn:aws:lambda:us-west-2:12345:function:customdomain-dev
  - Rest API URL: https://qxea58abcd.execute-api.us-west-2.amazonaws.com/api/

You now have an API up and running using API Gateway and Lambda:

$ curl https://qxea58abcd.execute-api.us-west-2.amazonaws.com/api/
{"hello": "world"}

The qxea58abcd.execute-api.us-west-2.amazonaws.com domain name was auto-generated by API Gateway. Replacing this domain name with our own custom domain name allows us to use simpler and more intuitive URLs that we can provide to our API users.

Configuring a Custom Domain

In this tutorial, we’re using Amazon Route53 to manage our DNS configuration. If you’re using a third-party domain registrar, the steps will be similar, but you will have to create your DNS records using your provider’s web interface or API.

For this tutorial, we’ll configure the domain chalice-demo-app.com. Be sure to replace this value with your own domain name.

Creating a Hosted Zone

First, we’ll need to create a hosted zone in Route 53. If you already have a hosted zone created for your domain, you can skip this step.

We’ll be using the AWS CLI V2 to configure our domain. You can follow the installation instructions if you don’t have the AWS CLI installed.

$ aws route53 create-hosted-zone --name chalice-demo-app.com --caller-reference 12345
{
    "Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/ZABCDEFGABCDEFGLDO822",
    "HostedZone": {
        "Id": "/hostedzone/ZABCDEFGABCDEFGLDO822",
        "Name": "chalice-demo-app.com.",
        "CallerReference": "12345",
        "Config": {
            "PrivateZone": false
        },
        "ResourceRecordSetCount": 2
    },
    "ChangeInfo": {
        "Id": "/change/C07395431VDLB0CY65VP",
        "Status": "PENDING",
        "SubmittedAt": "2020-07-21T17:13:54.709000+00:00"
    },
    "DelegationSet": {
        "NameServers": [
            "ns-123.awsdns-31.net",
            "ns-123.awsdns-05.com",
            "ns-123.awsdns-09.org",
            "ns-123.awsdns-40.co.uk"
        ]
    }
}

You’ll need to save the value of the hosted zone id for later. From the output above the line "Id": "/hostedzone/ZABCDEFGABCDEFGLDO822", contains our hosted zone id of ZABCDEFGABCDEFGLDO822. We’ll refer to this value as $OUR_HOSTED_ZONE_ID later.

You’ll now need to register the "NameServers" shown in the output above with your domain registrar.

Creating an ACM Certificate

Now that we have our hosted zone, we’ll need to create an ACM certificate associated with this domain. This is the SSL/TLS certificate that will be used when requests are made to our custom domain. In this example, we’ll create a wildcard certificate for *.chalice-demo-app.com. Note that we’re creating a REGIONAL endpoint type for our REST API, which means that our ACM certificate must be in the same region as our REST API. In this example, we’re using us-west-2. If you’re using the default EDGE endpoint type, the ACM cert must be in us-east-1. You can explicitly specify the region using the --region CLI parameter if needed.

$ aws acm request-certificate --domain-name "*.chalice-demo-app.com" \
    --validation-method DNS --idempotency-token 12345 \
    --options CertificateTransparencyLoggingPreference=DISABLED
{
    "CertificateArn": "arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b"
}

Save the value of CertificateArn shown in the output above. We’ll need this value when we configure our app to use this custom domain.

Before we can use this certificate, we need to validate this certificate. This process demonstrates that we own or control the domain name associated with the certificate. In the command above, we used the --validation-method DNS, which requires us to add CNAME records to validate we control our domain name. ACM supports both DNS validation as well as email validation.

To validate our domain, we’ll now create the necessary CNAME records in our hosted zone using the Route53 API. First, we need to retrieve the values for our CNAME record. Be sure to replace the value of --certificate-arn with your own certificate ARN in the command below:

$ aws acm describe-certificate --certificate-arn arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b \
    --query Certificate.DomainValidationOptions[0]
{
    "DomainName": "*.chalice-demo-app.com",
    "ValidationDomain": "*.chalice-demo-app.com",
    "ValidationStatus": "PENDING_VALIDATION",
    "ResourceRecord": {
        "Name": "_1234567891234567897eb5512d9fb554.chalice-demo-app.com.",
        "Type": "CNAME",
        "Value": "_123456789123456789e7495341c27cd1.jfrzftwwjs.acm-validations.aws."
    },
    "ValidationMethod": "DNS"
}

Next we’ll create a CNAME record for _1234567891234567897eb5512d9fb554.chalice-demo-app.com. with a value of _123456789123456789e7495341c27cd1.jfrzftwwjs.acm-validations.aws.:

$ aws route53 change-resource-record-sets \
    --hosted-zone-id $OUR_HOSTED_ZONE_ID --change-batch \
'{
  "Changes": [
    {
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "_0073e080112eb8de8c7eb5512d9fb554.chalice-demo-app.com.",
        "Type": "CNAME",
        "TTL": 300,
        "ResourceRecords": [{"Value": "_6e560a5a9831aad210e7495341c27cd1.jfrzftwwjs.acm-validations.aws."}]
      }
    }
  ]
}'

# Command output:
{
    "ChangeInfo": {
        "Id": "/change/C0339874QPDDRA8TKT7U",
        "Status": "PENDING",
        "SubmittedAt": "2020-07-21T17:36:39.902000+00:00"
    }
}

It will take a few minutes before ACM validates your domain. You can move on to the next steps, or if you’d like to wait until the domain is validated you can use the CLI’s certificate-validated waiter, which will block until the ACM certificate is validated:

$ aws acm wait certificate-validated \
    --certificate-arn arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b

Chalice App Configuration

Now that we have our hosted zone and ACM certificate created, we can configure our Chalice application with our custom domain. To do so we need to add api_gateway_custom_domain configuration option and specify our ACM certificate ARN as well as the our custom domain name. You’re .chalice/config.json file should look like this:

{
  "version": "2.0",
  "app_name": "customdomain",
  "api_gateway_endpoint_type": "REGIONAL",
  "stages": {
    "dev": {
      "api_gateway_custom_domain": {
        "domain_name": "api.chalice-demo-app.com",
        "certificate_arn": "arn:aws:acm:us-west-2:0123456789:certificate/578efbda-6bc7-4ae2-9964-6e6c3f58008b"
      },
      "api_gateway_stage": "api"
    }
  }
}

We we rerun the chalice deploy command you’ll notice there’s a new Custom domain name: line in the output:

$ chalice deploy
Creating deployment package.
Updating policy for IAM role: customdomain-dev
Updating lambda function: customdomain-dev
Updating rest API
Creating custom domain name: api.chalice-demo-app.com
Creating api mapping: /
Resources deployed:
  - Lambda ARN: arn:aws:lambda:us-west-2:0123456789:function:customdomain-dev
  - Rest API URL: https://qxea58abcd.execute-api.us-west-2.amazonaws.com/api/
  - Custom domain name:
      HostedZoneId: Z1UJRXOUMOOFQ8
      AliasDomainName: d-6vj4cynstd.execute-api.us-west-2.amazonaws.com

Now that we’ve configured our Chalice app with our custom domain, there’s one step left. We need to update our DNS configuration to point to our REST API.

To do this, we’ll use the values of HostedZoneId and AliasDomainName in the output above to create an alias record in our hosted zone.

Alias Record Configuration

You can run the following command to create an alias record to your REST API. Note that there are two different hosted zone id values here. The value specified as the --hosted-zone-id value is the ID of our hosted zone ID ($OUR_HOSTED_ZONE_ID) that we created earlier in this example. The value of the HostedZoneId in the AliasTarget section is the value of the HostedZoneId generated by API Gateway shown in the output of chalice deploy above.

$ aws route53 change-resource-record-sets --hosted-zone-id ZABCDEFGABCDEFGLDO822 --change-batch \
'{
  "Changes": [
    {
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "api.chalice-demo-app.com",
        "Type": "A",
        "AliasTarget": {
          "DNSName": "d-6vj4cynstd.execute-api.us-west-2.amazonaws.com",
          "HostedZoneId": "Z1UJRXOUMOOFQ8",
          "EvaluateTargetHealth": false
        }
      }
    }
  ]
}'

# Command output:
{
    "ChangeInfo": {
        "Id": "/change/C0539657Y0HMX8XBC5EH",
        "Status": "PENDING",
        "SubmittedAt": "2020-07-21T17:52:34.935000+00:00"
    }
}

Verification

Our Chalice application is now configured to use our custom domain. We can verify this by making a request to our custom domain. In this example, this is api.chalice-demo-app.com:

$ curl -i https://api.chalice-demo-app.com/
HTTP/1.1 200 OK
Date: Tue, 21 Jul 2020 17:56:00 GMT
Content-Type: application/json
Content-Length: 17
Connection: keep-alive
x-amzn-RequestId: 9f33fbb9-6b10-469e-827f-f287199c9bc5
x-amz-apigw-id: QCPXoEwPIAMFi8Q=
X-Amzn-Trace-Id: Root=1-5f172c30-dccc232932a16a539dfc01b9;Sampled=0

{"hello":"world"}

Next Steps

For more information on configuring custom domains, check out our topic guide on custom domains as well as the config file reference for the api_gateway_custom_domain and the websocket_api_custom_domain options.

Event Sources Tutorial →