Fastly

This add-on is operated by Fastly, Inc

Cache static, dynamic and streaming content to improve your app’s performance.

Fastly

Last Updated: 23 February 2015

Table of Contents

Fastly is the world’s smartest content delivery network. The Fastly Heroku add-on provides a convenient and easy way to add the power of a distributed content delivery network (CDN) to the rapid deployment and management of Heroku.

This document explains how to set up static and dynamic caching with Fastly on the Heroku platform. The quickest way to improve performance and get up and caching is to set up static caching and then iteratively add dynamic caching. Note that not all configuration steps are required to set up basic static caching and are clearly noted when you may want to skip a step.

Provisioning Fastly services

A list of plans available can be found on Fastly’s Heroku add-on page.

Fastly can be attached to a Heroku application via the command line interface (CLI):

$ heroku addons:add fastly:fast
-----> Adding fastly to sharp-mountain-4005... done, v18 ($50/mo)

Once Fastly has been added, three configuration variables become available in your application. You can view these using the heroku config:get command.

$ heroku config:get FASTLY_CDN_URL
yourappname-herokuapp-com.global.ssl.fastly.net
$ heroku config
FASTLY_API_KEY:    xyz1234abc
FASTLY_CDN_URL:    yourappname-herokuapp-com.global.ssl.fastly.net
FASTLY_SERVICE_ID: abcd12345

It may take up to 30 seconds for your configuration to complete. Once completed, your FASTLY_CDN_URL will be immediately available.

Set up and configuration

As soon as you provision Fastly we create a Service on your behalf with the following configuration options:

# e.g. a heroku app named 'fastly-test'
Service name: fastly-test
Backend address: fastly-test.herokuapp.com:80
Domain name: fastly-test-herokuapp-com.global.ssl.fastly.net

The Backend address represents the address of the dyno where we pull content from. The Domain name is how we route requests to your website.

This default configuration works for serving static content over HTTP and HTTPS.

If you only plan to use Fastly to cache static content like JavaScript and images, you can quickly complete your set up by jumping ahead to the configuring static content caching section.

To take full advantage of Fastly’s features like dynamic content caching and TCP-optimization continue reading.

CNAME your domain to Fastly

This step is not required if you only wish to cache static content - jump to static content caching. Creating a CNAME to Fastly requires first setting up a custom domain. Instructions can be found in the configuring a custom domain section.

In order to take full advantage of Fastly, direct your application’s traffic to flow through our network by CNAME-ing your top level domain so that it points to Fastly.

Directing your traffic through Fastly gives you a performance boost. We keep TCP-optimized sessions open between our cache nodes and your Heroku dynos to minimize transit time for requests.

In your DNS provider, CNAME your domain to point to your FASTLY_CDN_URL. Consult our DNS setup guide for step-by-step CNAME instructions for most major providers.

Once the DNS entry has propagated you should see users accessing the site via Fastly’s cache.

You can now continue to set up your application for static and dynamic caching.

Static content caching

Configuration for Rails (3.1+)

To cache static content (e.g., CSS, JavaScript, images) with Fastly and Ruby on Rails you must configure the asset pipeline to use Fastly as your asset_host and and set cache control headers.

If you didn’t set up a custom domain, set your asset_host to your FASTLY_CDN_URL. If you are using a custom domain and CNAME, you can still use your FASTLY_CDN_URL as your asset_host or simply ensure you set proper cache headers within your application.

Modify your Rails configuration by editing config/environments/production.rb with the following code (it may be commented out by default):

config.action_controller.asset_host = ENV['FASTLY_CDN_URL']

We use config/environments/production.rb here for production environments. You could also add this config to any environment you wish to use Fastly with (e.g., config/environments/staging.rb)

If you have content in the /public directory that you want Fastly to cache, you may also need to set:

config.serve_static_assets = true

Setting cache control headers

Cache control headers tell Fastly what content to cache and for how long to cache it. It’s crucial to set correct cache control headers that make sense for your application’s scenario. Generally, the longer you cache an object, the higher your cache hit ratio will be, yielding better performance.

With the Rails asset pipeline you can the set the cache control with the following setting:

config.static_cache_control = 'public, s-maxage=2592000, maxage=86400'

This will set up HTTP Cache-Control headers on assets that look like this:

Cache-Control: public, s-maxage=2592000, max-age=86400

In the Cache-Control directive above:

  • public specifies that this content can be cached
  • s-maxage specifies the amount of time in seconds (30 days in this example) to keep the content cached for “public caches” (i.e., Fastly caches)
  • max-age specifies an amount of time in seconds (24 hours in this example) to keep the content cached for “private caches” (i.e., the user’s browser)

The values above are provided as an example. Carefully consider what max-age values make the most sense and will provide the best performance for your application.

For more explanation and examples of setting Cache-Control headers for Fastly, read our cache control tutorial.

Setting up asset digests

This step is not required, but highly recommended to make it easier to keep cached assets up to date.

Asset digests (aka “fingerprinting”) are an easy way to know when asset content changes. They’re a good strategy to use without having to manually invalidate assets from the cache when they change.

Fingerprinting works by appending a hash of the file’s content onto the name of the file. This way the asset is “fingerprinted” and when the file changes, the filename also changes.

For example, if you have a file called fast.jpg and its content hash is 908e25f4bf641868d8683022a5b62f54 then the filename will be:

fast-908e25f4bf641868d8683022a5b62f54.jpg

To enabled digests with the asset pipeline, set the following in your environment configuration file:

config.assets.digest = true

For more information on digests with Rails, read the Rails Asset Pipeline Fingerprinting guide.

If you use a framework other than Rails, Sprockets is a popular asset packaging system.

Configuration with older versions of Rails

If you are using an older version of Rails that doesn’t use the asset pipeline, see our Rails 2 Sprockets integration documentation.

Configuration with Sinatra

To cache static assets with Sinatra, use the sinatra-asset-pipeline gem, which adds sprockets integration to Sinatra.

In a similar fashion to the Rails asset pipeline you can set an asset_host and cache control headers.

Configuration with Node

You can incorporate the express-cdn Node.js module to set up Fastly static asset caching with your Node.js applications.

The express-cdn docs do not explicitly call out use with Fastly, but it works exactly the same as their docs demonstrate.

In your app.js file (see this example) set the domain attribute to your FASTLY_CDN_URL (e.g., my-app.global.ssl.fastly.net) or your custom domain, if you are using one.

var options = {
    publicDir  : path.join(__dirname, 'public')
  , viewsDir   : path.join(__dirname, 'views')
  , domain     : process.env.FASTLY_CDN_URL
  , ...
  , production : true
};

Configuration with Python (Django)

If you’re using django-static you can specify the MEDIA_URL as your FASTLY_CDN_URL.

Configure a custom domain

To use a custom domain with Fastly and Heroku, you must complete this step.

To use a custom domain with Fastly and Heroku, you must create it using the Fastly API. Fastly needs this configuration information to be able to identify the correct routes to use to send traffic to your application. For example, to configure Heroku to use the custom domain fastly-test.com instead of fastly-test.herokuapp.com you must specifically configure fastly-test.com via the API.

You can use any of our language clients to interact with the API. We’ve provided a script in the fastly-ruby client to create a domain or you can use the instructions for bash/curl below.

Because all Fastly configuration changes are versioned, the process of adding a custom domain involves a few simple steps:

  1. Get the version list to locate the currently active version number.
  2. Clone the active version to create a new updateable version.
  3. Add, change, or remove any configuration as necessary.
  4. Activate this new version.

Versioning makes it easy to quickly roll back to a previous configuration if you make a mistake.

First, set up a few environment variables to save some typing later. The values for these variables can be grabbed from the Fastly Heroku dashboard or via the heroku config command.

$ export FASTLY_API_KEY="Fastly-Key:0a1b2c3d4e5f6"
$ export FASTLY_SERVICE_ID="0a1b2c3d4e5f6"
$ export FASTLY_API_URL="https://api.fastly.com/service/$FASTLY_SERVICE_ID"

Note that the FASTLY_API_KEY variable here contains the actual HTTP Header value for the Fastly-Key

You can view the Fastly environment variables you just set up with:

$ env | grep FASTLY
FASTLY_API_URL=https://api.fastly.com/service/0a1b2c3d4e5f6
FASTLY_SERVICE_ID=0a1b2c3d4e5f6
FASTLY_API_KEY=Fastly-Key:0a1b2c3d4e5f6

Then run this curl command:

$ curl -X GET -H $FASTLY_API_KEY "$FASTLY_API_URL/version"
# response formatted for readability
[{
"testing":null,
"locked":true,
"number":1,
"active":true,
"service_id":"0a1b2c3d4e5f6",
"staging":null,
"created_at":"2014-07-08T13:30:42+00:00",
"deleted_at":null,
"comment":"",
"updated_at":"2014-07-08T13:30:44+00:00",
"deployed":null,
"inherit_service_id":null
}]

The response above tells you that the service only has one configuration version "number": 1, that it’s locked "locked":true, and that it’s active "active":true. Using this information, you can clone this to a new, editable version with the following command:

$ curl -X PUT -H $FASTLY_API_KEY "$FASTLY_API_URL/version/1/clone"
{"testing":false,"locked":false,"number":2,"active":false,"service_id":"0a1b2c3d4e5f6","staging":false,"created_at":"2014-07-08T13:30:42+00:00","deleted_at":null,"comment":"","updated_at":"2014-07-08T13:30:44+00:00","deployed":false,"inherit_service_id":null}

The response above tells you that the new version is now two "number":2, that it’s not locked "locked":false, and that it’s not active "active":false. Using this information, you can now add domains to this version with the following command:

$ curl -X POST -d "name=fastly-test.com" -H $FASTLY_API_KEY "$FASTLY_API_URL/version/2/domain"
{"name":"fastly-test.com","service_id":"0a1b2c3d4e5f6","version":2,"comment":""}

Once you have added the domains to the new version, you can make them active and ready to accept traffic:

$ curl -X PUT -H $FASTLY_API_KEY "$FASTLY_API_URL/version/2/activate"
{"testing":null,"locked":true,"number":2,"active":true,"service_id":"0a1b2c3d4e5f6","staging":null,"created_at":"2014-07-08T15:21:08+00:00","deleted_at":null,"comment":"","updated_at":"2014-07-08T15:21:08+00:00","deployed":null,"inherit_service_id":null,"msg":null}

From the output above, you can see that version two of the configuration is now locked and active. When making any version active it will be locked to ensure that no changes can be made that might cause unexpected issues.

Dynamic content caching

To use Fastly dynamic caching, remember to CNAME your domain to Fastly first.

Because Fastly is unlike other CDNs, we can cache rapidly changing dynamic content like APIs and dynamic HTML at the Fastly edge. Caching dynamic content with Fastly involves integrating your application with our instant purge API. We provide framework-specific plugins to make it easier and faster to set up, which are listed below.

In general, the process to dynamically cache an HTTP endpoint or object is:

  1. Create a cache key that maps to an object or set of objects.
  2. Set Cache-Control and Surrogate-Key HTTP headers on responses (HTTP GETs). Surrogate-Keys are HTTP headers that Fastly uses to associate cache keys with objects.
  3. Issue a purge with your cache key when the object changes (e.g. HTTP POSTs, PUTs, DELETEs).

We’ve written a blog post that explains at a high level more about caching dynamic content. In addition, our API caching blog post series is a good place to read about dynamic API caching in more depth.

Rails

Use the Fastly Rails plugin to cache dynamic content with Rails. The plugin contains helpers for creating cache keys, setting the required headers, and issuing purges.

Node

Use the Fastly plugin created by @thisandagain on github. It includes helpers for using the purge API.

Drupal

Use our Drupal plugin to automatically integrate dynamic caching into Drupal applications. The module can be downloaded from the link and installed into your site.

Wordpress

Use our Wordpress plugin to automatically integrate dynamic caching. The plugin can be install from source or from the Wordpress plugin directory.

Other

Are you using a framework not listed here and want to add Fastly dynamic caching to your site? Let us know! We do our best to make it as easy as possible for all developers to integrate with Fastly.

SSL

The default Fastly configuration supports HTTPS over SSL out of the box and is enough for most of our Heroku users. If you need advanced SSL support, several different options are available including custom certificate hosting and use. These options are available for an extra cost. See our SSL pricing guide for more information about all the available options. Setting up a custom SSL certificate requires upgrading to a custom heroku plan. Email support@fastly.com to get set up.

Verifying your setup

Make sure you’ve deployed the code changes listed above that configure your application for use with Fastly.

Verifying static content

You can verify that static assets are coming from the CDN by visiting your application in the browser and then viewing source. Any assets that would be served by the asset pipeline such as application.css should be coming from the domain that matches your FASTLY_CDN_URL such as:

http://fastly-test.global.ssl.fastly.net/assets/application-048b5b7c55e3a7429fda3.css

If the domain does not match the current value of your FASTLY_CDN_URL you should double check that your application and assets are configured correctly and that the most recent code changes have been deployed. If you’re using a custom domain and did not configure a static asset host, you should see your custom domain here.

If you see the right URL but the wrong content, try viewing that content directly by copying and pasting your asset URL into your browser’s address bar. If you are getting an error, verify that your application is serving the right content. You can do this by taking the path in this example /assets/application-048b5b7c55e3a7429fda3.css, and trying to view that file from your own domain instead of through the CDN. So if your application was hosted on fastly-test.com you could visit:

http://fastly-test.com/assets/application-048b5b7c55e3a7429fda3.css

If the asset does not render, there is probably nothing wrong with your Fastly configuration, but rather the problem most likely exists in your application. Check the deploy output to make sure there were no errors.

Another useful tool is to verify the files exist on the dyno as you expect. You can do this by running:

$ heroku run bash

Then use cd and ls to verify the presence of files and cat to verify the content.

If you’re confident that your application is serving the correct file but you’re not seeing the right thing coming through the CDN, you can issue a “Purge All” from the Fastly dashboard. This will remove all objects from the cache and subsequently update them to the latest content on your Heroku application.

Use the “Purge All” button with caution! It could take a long time to refresh the cache and you could see higher load on your application dynos as a result of the cache filling up.

Verifying dynamic content

You can use curl to make your request and verify that the response contains certain headers.

curl -I https://fastly-todo.com/path/to/thing.json

If your request is going through Fastly, you should see some headers that look like:

HTTP/1.1 200 OK
...
Cache-Control: public, s-maxage=2592000, maxage=86400
...
Via: 1.1 varnish
Age: 12
X-Served-By: cache-sjc3127-SJC
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1406260210.511119,VS0,VE0

If your response doesn’t include some variation of some of these headers, you probably have your application configured incorrectly.

Troubleshooting and FAQs

Below are solutions to some common issues encountered by our users during setup.

Content not found and 404’s

As long as the content is available from the application, we will never return a 404 (due to the “proxy” nature of the Fastly caches).

Redirect loops and forcing HTTP to HTTPS

If your application forces redirects from HTTP to HTTPS and upon installation of Fastly you see redirect loops coming from the cache, this is because you need to configure Fastly to pull content from your apps over HTTPS/SSL. By default we cache 301 redirects, which causes this redirect loop to happen.

You can fix this by updating your port to 443 under “Origin Settings” in the Fastly Heroku dashboard. Depending on what TTL value you use, you may need to wait for the 301 redirects to expire in the cache. If you can’t wait or your TTL is very large, you can issue a Purge All from the dashboard, which will force a refresh - removing all the cached 301s and updating with the actual content, which will now be pulled over port 443 (used by SSL).

Expired assets

It is possible for our caches to serve an expired asset if the client makes a request for an expired asset and the origin dyno(s) becomes unavailable. You can tune this by setting the TTL (cache expiration time) with cache control headers.

Cross-origin resource sharing (CORS)

If you encounter trouble loading assets (such as fonts, especially in FireFox) that are hosted somewhere other than where your application dynos, you should first ensure that the host is correctly setting the Access-Control-Allow-Origin header. If you have verified your configuration and the problem persists, you can set a CORS Access-Control-Allow-Origin header at the edge via the Fastly configuration API.

The process for setting a CORS header using bash/curl is below. You can also do this via any of our client libraries

# Get the versions for your service
curl -XGET -H 'Fastly-Key:<fastly_api_key>' https://api.fastly.com/service/<service_id>/version

# Grab the version number that is active (active=1)
# Clone the configuration to a new version
curl -XPUT -H 'Fastly-Key:<fastly_api_key>' https://api.fastly.com/service/<service_id>/version/<current_version_number>/clone

# Create the header
curl -XPOST -H 'Fastly-Key:<fastly_api_key>' -d 'name=Cors Allow&type=response&action=set&dst=http.Access-Control-Allow-Origin&src="*"&ignore_if_set=0&priority=10' https://api.fastly.com/service/<service_id>/version/<cloned_version_number>/header

# Activate the new version
curl -XPUT -H 'Fastly-Key:<fastly_api_key>' https://api.fastly.com/service/<service_id>/version/<cloned version number>/activate

Pricing

Based on the pricing plan you select, Fastly charges a flat rate on a monthly basis for its CDN services. We allocate a specific amount of bandwidth and set number of requests for the plan you select. Be sure to choose the plan that properly corresponds to your site’s traffic profile.

Migrating between plans

You can migrate between plans at any time. Migrating to a different plan does not affect any content we currently cache. Migrating your plan will only modify your plan features, limits, and price.

Use the heroku addons:upgrade command to migrate to a new plan:

$ heroku addons:upgrade fastly:pro
-----> Upgrading fastly:pro to sharp-mountain-4005... done, v18 ($100/mo)
       Your plan has been updated to: fastly:pro

Removing the Fastly add-on

Removing the Fastly add-on will destroy all associated data, configuration, and services and cannot be undone!

Fastly can be removed via the CLI:

$ heroku addons:remove fastly
-----> Removing fastly from sharp-mountain-4005... done, v20 ($50/mo)

Support

Support is available through our various Support channels.

You can always find us in the #fastly IRC channel on freenode.

We are also on twitter at @Fastly.