This add-on is operated by Mixable, Inc.
Speed up your app with the AWS CloudFront Content Delivery Network (CDN)
Edge
Last updated January 30, 2022
Table of Contents
Edge is a content delivery network (CDN) add-on for delivering your Heroku app to your users with low latency and high transfer speeds.
Adding a CDN to your application distributes its content across a global network with over 100 points of presence in 24 countries, ensuring high availability, scalability, and performance for your customers all over the world. This speeds up the delivery of static assets (images, style sheets, JavaScript files, etc.) to your users and reduces the load on your app.
The Edge CDN improves your customer experience by enabling HTTP/2, which efficiently parallelizes many requests to your application within a single connection with the customer.
Edge also improves your application security by negotiating SSL/TLS connections with the highest security ciphers and implementing transparent Distributed-Denial-of-Service (DDOS) mitigation for your app.
Edge is powered by Amazon CloudFront, which guarantees that it is a secure, reliable, and performant addition to your application.
The Edge CDN supports applications written in any Heroku-supported language and is managed via the Edge dashboard.
Provisioning the add-on
Edge can be attached to a Heroku application via the CLI:
A list of all plans available can be found here.
$ heroku addons:create edge --domain myapp.mycompany.com
Successfully configured https://d393bns1jna2o2.cloudfront.net
Created edge-animated-30784 as EDGE_AWS_ACCESS_KEY_ID, EDGE_AWS_SECRET_ACCESS_KEY, EDGE_DISTRIBUTION_ID, EDGE_URL
Use heroku addons:docs edge to view documentation
The optional --domain
allows you to configure a custom domain name during provisioning. See custom domain setup for more information.
After you provision Edge, the EDGE_URL
config var is available in your app’s configuration. It contains a hostname that serves your application through the CDN. You can confirm this via the heroku config:get
command:
$ heroku config:get EDGE_URL
https://d393bns1jna2o2.cloudfront.net
You can also confirm that your app is available at the hostname and its content is is served from the CDN (note the x-cache HTTP header):
$ curl -i https://d393bns1jna2o2.cloudfront.net
HTTP/2 200
content-type: text/html; charset=utf-8
server: WEBrick/1.3.1 (Ruby/1.9.2/2014-08-07)
date: Sun, 10 Dec 2017 16:29:10 GMT
via: 1.1 vegur, 1.1 cac0807f4e1bdd7cf57c08992aa341a5.cloudfront.net (CloudFront)
age: 6
x-cache: Hit from cloudfront
x-amz-cf-id: ADlk4cNUe2LVHzExvTM6pn0Nfhhsbnqla9TSvyOHFVmduDuhv0OFJw==
<html>
...
It may initially take up to 10 minutes to configure the CDN and deploy your content globally before your app is available at the EDGE_URL
.
If you are using a custom domain, you need to configure a DNS CNAME to the EDGE_URL
. See custom domain setup for more information.
Custom domain setup
You often don’t want to use the https://d393bns1jna2o2.cloudfront.net
hostname directly for your app. Edge allows you to configure a custom domain (e.g., https://myapp.mycompany.com
) that you can use instead.
Setup when installing
You can set an optional custom domain when installing the addon:
$ heroku addons:create edge --domain myapp.mycompany.com
Setup in the dashboard
You can also set or update a custom domain at any time via the Edge dashboard:
- Open the Edge dashboard.
- Click Edit next to the Custom Domain field.
- Enter your custom domain (e.g.,
myapp.mycompany.com
). - Click Save.
You will see that the distribution status is now “In Progress” configuring the custom domain.
DNS CNAME
Now you need to set up a DNS CNAME for your custom domain (e.g., myapp.mycompany.com
) to the Edge hostname (e.g., d393bns1jna2o2.cloudfront.net
). Consult with your DNS provider for specific instructions to create CNAME records.
You can confirm that your DNS is configured correctly by using the host
command, assuming your DNS changes have propagated:
$ host myapp.mycompany.com
myapp.mycompany.com is an alias for d393bns1jna2o2.cloudfront.net.
...
After the DNS CNAME record has propagated, you can access your app on the custom domain:
$ curl -vi https://myapp.mycompany.com
HTTP/2 200
content-type: text/html; charset=utf-8
server: WEBrick/1.3.1 (Ruby/1.9.2/2014-08-07)
date: Sun, 03 Dec 2017 00:38:47 GMT
via: 1.1 vegur, 1.1 022c901b294fedd7074704d46fce9819.cloudfront.net (CloudFront)
age: 88
x-cache: Hit from cloudfront
x-amz-cf-id: ysBQ-8gCY-eWxmjDXMMVjucfBNbLvP650ut28b99HeZqz-8vlIEt6g==
<html>
...
## Disable HTTPS validation to see content over HTTPS
$ curl -k https://myapp.mycompany.com
<html>
...
HTTPS / SSL
When you access your custom domain over HTTPS (e.g. https://myapp.mycompany.com
), you will see a security warning that the SSL certificate is for *.cloudfront.net
. To fix this, you need to approve a free SSL certificate for your custom domain, then apply it to your distribution.
Certificate Request
When you set a custom domain name, Edge sends a certificate request email. Emails are sent to domain owners listed in whois
as well as special email addresses like admin@mycompany.com
.
You can see your progress toward the approval process and apply the certificate through the Edge dashboard:
- Open the Edge dashboard.
- See “Pending Validation” next in the “Custom Cert” field.
- Find the “Certificate Approval” email in your (or the domain owner’s) inbox.
- Click the “Amazon Certificate Approvals” link.
- Click I Approve.
- Return to the Edge dashboard and see “Issued” in the “Custom Cert” field.
- Click Apply.
You will see that the distribution status is now “In Progress” configuring the custom certificate.
## HTTPS works as expected
$ curl https://myapp.mycompany.com
<html>
...
If you did not receive the email, check your domain and email settings, then click Resend in the Edge dashboard.
For more information, see the AWS Certificate Manager Email Validation FAQ.
Cache control
By default, objects stay cached for 24 hours. This duration results in better performance for your users because your content is more likely to be served directly from the edge cache. It also reduces load on your Heroku app.
You can control this duration from your app with the Cache-Control
headers.
To set a shorter duration, say one hour, respond with the header Cache-Control: max-age=3600
.
To set a longer duration, say a year, respond with the header Cache-Control: max-age=31536000
.
To bypass caching altogether respond with Cache-Control: no-cache
.
For more information read the Using Headers to Control Cache Duration for Individual Objects CloudFront doc.
CloudFront configuration
For reference, the CloudFront cache behavior settings are:
Setting | Value |
---|---|
Viewer Protocol Policy | Redirect HTTP to HTTPS |
Allowed HTTP Methods | GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE |
Cached HTTP Methods | GET, HEAD |
Cache Based on Selected Request Headers | None (Improves caching) |
Object Caching | Use Origin Cache Headers |
Minimum TTL | 0 |
Maximum TTL | 31536000 |
Default TTL | 86400 |
Forward Cookies | None (Improves caching) |
Query String Forwarding and Caching | None (Improves caching) |
Compress Objects Automatically | Yes |
You can update these settings with the AWS CLI or opening a support ticket.
Using with the AWS CLI
You can update the Edge config or create an invalidation via the aws
CLI. This entails installing the aws
CLI, and configuring it with EDGE_AWS_ACCESS_KEY_ID
, etc.
If you have… | Install with… |
---|---|
Mac OS X | brew install awscli |
Windows | Python / PIP instructions |
Ubuntu Linux | apt-get install awscli |
Other | Python / PIP instructions |
Configure and activate an AWS CLI Profile
Configure a new edge
profile with your addon AWS credentials:
$ heroku config --app edgeapp
EDGE_AWS_ACCESS_KEY_ID: AKIA...
EDGE_AWS_SECRET_ACCESS_KEY: JRHH...
EDGE_DISTRIBUTION_ID: EJM2O0DPZ8B2Y
EDGE_URL: https://d1unsc88mkka3m.cloudfront.net
$ aws configure --profile edge
AWS Access Key ID [None]: AKIA...
AWS Secret Access Key [None]: JRHH...
Default region name [None]: us-east-1
Default output format [None]: json
Activate the profile:
$ export AWS_DEFAULT_PROFILE=edge
Use the CLI to inspect your distribution
Test the CLI. You can get your configuration:
$ export AWS_DEFAULT_PROFILE=edge
$ export DISTRIBUTION_ID=EJM2O0DPZ8B2Y
$ aws cloudfront get-distribution --id $DISTRIBUTION_ID
{
"ETag": "E1H92KNENJ9W16",
"Distribution": {
"Id": "EJM2O0DPZ8B2Y",
...
}
}
But you can not update it:
$ aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths '/*'
An error occurred (AccessDenied) when calling the CreateInvalidation operation: User is not authorized to perform: cloudfront:CreateInvalidation
Install AWS CLI plugin with pip
Install the AWS CLI Execute API plugin. The plugin lets you send all CloudFront Update API requests to an endpoint scoped to your Heroku addon.
If you installed awscli
with Homebrew, use its bundled Python and note the site-package directory with pip show awscli-plugin-execute-api
:
$ /opt/homebrew/Cellar/awscli/2.4.7/libexec/bin/pip install --upgrade awscli-plugin-execute-api
Successfully installed awscli-plugin-execute-api-0.2.1
$ /opt/homebrew/Cellar/awscli/2.4.7/libexec/bin/pip show awscli-plugin-execute-api
Name: awscli-plugin-execute-api
Location: /opt/homebrew/Cellar/awscli/2.4.7/libexec/lib/python3.9/site-packages
If you installed awscli
with pip
, use your system Python and note the site-package directory with pip show awscli-plugin-execute-api
:
$ pip install --upgrade awscli-plugin-execute-api
$ pip show awscli-plugin-execute-api
Name: awscli-plugin-execute-api
Location: /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages
Configure the plugin
$ export AWS_DEFAULT_PROFILE=edge
## use the location of site-packages from `pip show`
$ aws configure set plugins.cli_legacy_plugin_path /opt/homebrew/Cellar/awscli/2.4.7/libexec/lib/python3.9/site-packages
## configure configure the plugin and endpoints
$ aws configure set plugins.execute-api awscli_plugin_execute_api
$ aws configure set cloudfront.update-distribution https://api.edge.mixable.net/cloudfront
$ aws configure set cloudfront.create-invalidation https://api.edge.mixable.net/cloudfront
You can verify its config in ~/.aws/config
:
[profile edge]
region = us-east-1
output = json
cloudfront =
update-distribution = https://api.edge.mixable.net/cloudfront
create-invalidation = https://api.edge.mixable.net/cloudfront
[plugins]
execute-api = awscli_plugin_execute_api
cli_legacy_plugin_path = /opt/homebrew/Cellar/awscli/2.4.7/libexec/lib/python3.9/site-packages
Use the CLI to update your distribution
Command to create an invalidation:
$ aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths '/*'
Example response:
{
"Location": "https://cloudfront.amazonaws.com/2018-11-05/distribution/EQ0A1ULE1WVTI/invalidation/ILN6EOUA0GR8R",
"Invalidation": {
"Id": "ILN6EOUA0GR8R",
"Status": "InProgress",
"CreateTime": "2019-06-19T15:45:39Z",
"InvalidationBatch": {
"Paths": {
"Quantity": 1,
"Items": [
"/*"
]
},
"CallerReference": "cli-1560959138-179155"
}
}
}
Command to get config:
$ aws cloudfront get-distribution-config --id $DISTRIBUTION_ID --query 'DistributionConfig' > config.json
Now update config.json to add behaviors, etc.
Command to update config:
$ ETAG=$(aws cloudfront get-distribution --id $DISTRIBUTION_ID --output text --query 'ETag')
$ aws cloudfront update-distribution --id $DISTRIBUTION_ID --if-match $ETAG --distribution-config file://config.json
Example output:
{
"ETag": "E2ZJ47QDF7LPIS",
"Distribution": {
"Id": "EQ0A1ULE1WVTI",
"ARN": "arn:aws:cloudfront::615670401552:distribution/EQ0A1ULE1WVTI",
"Status": "InProgress",
...
}
}
Using with Rails 3.1 and above
Rails 3.1 introduced an asset pipeline for building static assets like CSS, HTML and JavaScript files with pre-processors such as Sass or ERB.
Using Edge to distribute these assets is a one-line change. In config/environments/production.rb
, set:
config.action_controller.asset_host = ENV['EDGE_URL']
This tells Rails to use your Edge endpoint as the hostname for static assets.
If you are using a custom domain, you should use that hostname instead of the EDGE_URL
cloudfront.com
hostname.
Note that you do not have to use the discouraged Asset Sync gem to copy assets to S3. Your Rails app can serve assets directly and the CDN will cache the contents for fast delivery to end users.
For more information, see:
- Ruby on Rails The Asset Pipeline CDNs guide
- Heroku Dev Center Using Amazon CloudFront CDN doc
- AWS Caching the Rails Asset Pipeline with Amazon CloudFront blog
Using with Python/Django
Django 2.0 has a “static files” app that collects CSS, images and JavaScript into a single location to be served in production.
Using Edge to distribute these assets is simple to configure. In settings.py
, add:
INSTALLED_APPS = [
'django.contrib.staticfiles',
]
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = os.environ.get('EDGE_URL', '') + '/static/'
Now you can use the collectstatic
tool to collect the apps static files in into the ./static
directory:
$ python manage.py collectstatic
And you can use the static
helper in your templates:
{% load static %}
<link rel="stylesheet" href="{% static "style.css" %}" type="text/css" media="screen" />
Note that you do not have to use the S3Storage backend to copy static files to S3. Your Django app can serve the assets directly and the CDN will cache the contents for fast delivery to end users.
For more information, see:
- Django Managing Static Files howto
- Django staticfiles app reference
Dashboard
The Edge dashboard allows you to configure a custom domain name, apply a custom SSL certificate, and invalidate the distribution contents.
You can access the dashboard via the CLI:
$ heroku addons:open edge
Opening https://addons-sso.heroku.com/apps/...
or by visiting the Heroku Dashboard and selecting the application in question. Select Edge from the Add-ons menu.
Troubleshooting
If you do not see a certificate approval email, you might not be a registered owner of the domain name. You can look at WHOIS for the domain name to see contact information, and ask one of the owners to approve the certificate for you.
Migrating between plans
Use the heroku addons:upgrade
command to migrate to a new plan.
$ heroku addons:upgrade edge:newplan
-----> Upgrading edge:newplan to sharp-mountain-4005... done, v18 ($49/mo)
Your plan has been updated to: edge:newplan
Removing the add-on
You can remove Edge via the CLI:
This will disable the CDN hostname and cannot be undone!
$ heroku addons:destroy edge
-----> Removing edge from sharp-mountain-4005... done, v20 (free)
Before removing Edge, you should make sure your users are not accessing your app through the CDN.
Support
All Edge support and runtime issues should be submitted via one of the Heroku Support channels. Any non-support related issues or product feedback is welcome at noah@mixable.net.