An official website of the United States government US flag signifying that this is a United States Federal Government website

CDN service

CDN service

This service provides three key elements to support production applications:

  1. Custom domain support, so that your application can have your domain instead of the default *.app.cloud.gov domain.
  2. Content Distribution Network (CDN) caching (using AWS CloudFront), for fast delivery of content to your users. Before setting up this service, review how the CDN works.
  3. HTTPS support via free TLS certificates with auto-renewal (using Let’s Encrypt), so that user traffic is encrypted.

Plans

Plan Name Description Price
cdn-route Custom domains, CDN caching, and TLS certificates with automatic renewal Free

Options

Name Required Description Default
domain Required Your custom domain (or domains separated by commas)
origin Optional Don’t put this in your command for cloud.gov tenant applications. For services/applications that are not cloud.gov tenant applications (more info): the origin root URL of the application
path Optional The path for the application within the main domain supplied ""
insecure_origin Optional Read the application over HTTP instead of HTTPS false
cookies Optional Forward cookies to the origin true

How to create an instance of this service

Use these instructions for cloud.gov tenant applications. If you’re creating a custom domain for something else (such as a public S3 bucket), see external services and applications.

Before you begin, note that once you initiate creation of a CDN service instance, you can’t update or delete it until it has been created successfully. Typos in the service creation parameters can cause creation to get stuck in a pending state. If you’re using DNSSEC, verify your DNSSEC configuration because invalid DNSSEC configuration will also cause creation to get stuck.

First, target the space your application is running in:

cf target -o <org> -s <space>

Create a private domain in your organization (replace <org> with your org name, and replace my.example.gov with your domain):

cf create-domain <org> my.example.gov

Then, to create a cdn-route service instance, run the following command (replace my-cdn-route with a name for your service instance, and replace my.example.gov with your domain):

cf create-service cdn-route cdn-route my-cdn-route \
    -c '{"domain": "my.example.gov"}'

(The command includes cdn-route cdn-route because cdn-route is the name of the service and the name of the service plan.)

If you have more than one domain, you can pass a comma-delimited list to the domain parameter (just keep in mind that the broker will wait until all domains are CNAME’d, as explained in the next step):

cf create-service cdn-route cdn-route my-cdn-route \
    -c '{"domain": "my.example.gov,www.my.example.gov"}'

The maximum number of domains that can be associated with a single cdn-route service instance is 100.

How to set up DNS

Note: If you are creating a new site on cloud.gov or you are migrating an existing site to cloud.gov that can tolerate a small amount of downtime during the migration, you can skip the first step and proceed directly to Create CNAME record(s)

Step 1: Create TXT record(s)

Once you create the service instance, you need to retrieve the instructions to set up your DNS. Run the following command, replacing my-cdn-route with the service instance name you used in the previous step.

$ cf service my-cdn-route

Last Operation
Status: create in progress
Message: Provisioning in progress [my.example.gov => cdn-broker-origin.fr.cloud.gov]; CNAME or ALIAS domain my.example.gov to d3nrs0916m1mk2.cloudfront.net or create TXT record(s):
name: _acme-my.example.gov., value: ngd2suc9gwUnH3btm7N6hSU7sBbNp-qYtSPYyny325E, ttl: 120

Create the TXT record(s) as instructed by the broker. The existence of these records will be validated by Let’s Encrypt when issuing your certificate and will not affect your site.

After the records have been created wait up to 1 hour for the certificate to be provisioned. Your certificate has been provisioned when the cf service my-cdn-route command reports the status as create succeeded.

Last Operation
Status: create succeeded
Message: Service instance provisioned [my.example.gov => cdn-broker-origin.fr.cloud.gov]; CDN domain d3nrs0916m1mk2.cloudfront.net

Step 2: Create CNAME record(s)

Once the TXT records have been validated, or if you’ve decided to skip that step, you need to point your custom domain at the CDN. Run cf service my-cdn-route with the service instance name you choose.

Last Operation
Status: create succeeded
Message: Service instance provisioned [my.example.gov => cdn-broker-origin.fr.cloud.gov]; CDN domain d3nrs0916m1mk2.cloudfront.net

The output will include the CDN domain the broker has created for you. In this case, you need to create a CNAME record in your DNS server pointing my.example.gov to d3nrs0916m1mk2.cloudfront.net..

After the record is created, wait up to one hour for the CloudFront distribution to be provisioned and the DNS changes to propagate. Then visit your custom domain and see whether you have a valid certificate (in other words, that visiting your site in a modern browser doesn’t give you a certificate warning).

If you’ve waited more than two hours without a valid certificate appearing, contact support to check for problems.

Step 3: Map the route to your application

You need to map the domain you created to your application.

You can do that by adding the domain(s) to your application manifest.yml file under the routes section:

...
routes:
- route: my.example.gov

Then deploy your application with your updated manifest.

Alternate option: If you don’t want to put this in your manifest, you can manually add the route to your application:

cf map-route APPNAME my.example.gov

Troubleshooting

If nothing has changed when you visit your custom domain:

  • Make sure you’ve waited at least 30 minutes.
  • Check your DNS setup to make sure you completed the CNAME record creation.
  • If your custom domain uses DNSSEC, verify your DNSSEC configuration.

If you get the following error message when you try to update or delete a service instance: "Server error, status code: 409, error code: 60016, message: An operation for service instance [name] is in progress. – this happens because you can’t do anything to a service instance while it’s in a pending state. A CDN service instance stays pending until it detects the CNAME or ALIAS record. If this causes a problem for you, you can ask support to manually delete the pending instance.

If you’re working with many subdomains for the same domain name, Let’s Encrypt has a rate limit of 20 certificates per registered domain per week. If a cf create-service command times out due to certificate rate limiting, and shows in a failed state, you can use cf update-service to restart the certificate attempts.

How to update a service instance

To update a service instance, run the following command (replace my-cdn-route with your service instance name, and replace my.example.gov with your domain):

cf update-service my-cdn-route -c '{"domain": "my.example.gov"}'

Similarly to instance creation, after the record is updated, wait up to 30 minutes for the CloudFront distribution to be updated and the DNS changes to propagate. In addition, you may have to clear your browser’s cache if it shows the old content after the DNS changes propagate.

When to update the DNS

You only need to add a CNAME entry when you update the domain field. If you do, follow “How to set up DNS” again.

Deleting a service instance

After you delete an existing service instance, you may need to wait up to 30 minutes for the changes to complete and propagate.

External services and applications

To create a custom domain for a service or application that is not a cloud.gov tenant application (such as a public S3 bucket), put in the origin option:

cf create-service cdn-route cdn-route my-cdn-route \
    -c '{"domain": "my.example.gov", "origin": "my-app.external-example.gov"}'

Unlike creating custom domains for cloud.gov tenant applications, you don’t need to run the cf create-domain command first – there’s no need to create a private domain for externally-managed services and applications.

Then set up DNS.

More about how the CDN works

Caching

CloudFront uses your application’s Cache-Control or Expires HTTP headers to determine how long to cache content. If your application does not provide these headers, CloudFront will use a default timeout of 24 hours. This can be particularly confusing as different requests might be routed to different CloudFront Edge endpoints.

While there is no mechanism for cloud.gov users to trigger a cache clear, cloud.gov support can. Cache invalidation is not instantaneous; Amazon recommends expecting a lag time of 10-15 minutes (more if there are many distinct endpoints).

Authentication

Cookies are passed through the CDN by default, meaning that cookie-based authentication will work as expected. If you don’t want to forward cookies to your origin, you can disable this by setting the cookies parameter to false:

cf create-service cdn-route cdn-route my-cdn-route \
    -c '{"domain": "my.example.gov", "cookies": false}'

Other headers, such as HTTP auth, are stripped by default.

If you need a different configuration, contact cloud.gov support.

Certificate validity and renewal

Let’s Encrypt TLS certificates are valid for 90 days. The broker will automatically renew your certificate every 60 days. This process is usually immedate but can take several days to complete. If your certificate is expiring within the next 21 days and has not been renewed automatically, contact cloud.gov support.

The broker in GitHub

You can find the broker here: https://github.com/18F/cf-cdn-service-broker.