Speeding up asset delivery from your Rails app
Posted in: CDN, Getting Started   -   August 26, 2013

We all want our pages to load faster. Though the big picture of web performance is quite complex, payload size and location are two factors that can be minimized with a little effort. Here, we’ll look at how delivering static assets from a cookie-free domain via a content delivery network (CDN) can help reduce your latency.

Defining a cookie-free domain

Your Rails server has no use for cookies when serving static assets. It’s important to realize that once your app sets a cookie for your domain, all subsequent requests on that domain will send the cookie. This may be insignificant depending on the size of the cookie, but it’s essentially wasted bandwidth. Serving your assets on a different domain which doesn’t set cookies is an easy solution, and one that’s recommended by Yahoo Best Practices. For example, though our primary domain is challengepost.com, you’ll notice that our images, javascripts and stylesheets originate from assetspost.com.

Defining a CDN

A CDN delivers data from a nearby server, determined by geo-location, rather than making the full trip to your servers. When most of your users come from one place, this may not matter as much. But ChallengePost hosts local hackathons in countries around the world, like Way Out West Hack Battle in Sweden and Random Hacks of Kindness in South Africa. We also host online challenges with international appeal, such as the Chart.js Personal Dashboard Challenge and the Tactrick Android DevCup, so providing our global audience with a solid user experience is important to us.

Here’s how we do it:

The basics: setting a cookie-free domain

Let’s set the cookie-free domain first. You have two alternatives: as a subdomain or as a different domain altogether. Your choice probably depends on your business needs, but all other things equal, there isn’t much difference between the two other than the setup.

A. Subdomain

To scope cookies to one subdomain (e.g. www.example.com), so you can host assets from another (such as `assets.example.com), here’s what you’ll want in config/initializers/session_store.rb:

You also need to let Rails know which domain your assets will be served from so helper tags will construct the correct URLs. In config/production.rb:

Next up is your DNS configuration. You’ll want to make sure you have a CNAME (Canonical Name) record that aliases your assets subdomain to your primary A record domain. So for our example, the label/name/alias is assets.example.com and the value is www.example.com.

B. Separate domain

To share cookies across all subdomains of your primary, your session store will declare your top-level domain as the scope and you’ll need to acquire a different domain for your assets, like assets-example.com.

In config/production.rb:

In this case, you can add an A record for your assets domain that points to the same IP as your app server’s domain:

Now you have the ability to serve all assets on a cookie-free domain. When choosing where to set cookies in your app server, you’ll also want to consider how other cookie-setting services you rely on, such as Google Analytics, will be affected.

Next steps: setting up a CDN pull zone

At ChallengePost, we rely on the CDN provider MaxCDN, powered by the NetDNA CDN platform, to serve our static assets. Popular alternatives include Cloudflare and Amazon CloudFront. When setting up a CDN for the first time, the big question is “How do we put our content there?” It turns out you’ve got a few choices and it helps to understand two general strategies — push zones and pull zones — each with its own set of advantages and disadvantages.

In a push zone, developers are responsible for transferring content to the CDN directly. If you change assets and deploy regularly, you’ll want to automate this process to save a lot of hassle. With a pull zone, you simply deploy your assets to your own servers as usual and set up your CDN to fetch and cache the assets from your origin when requested. This approach is easier to set up, but results added latency for new assets.

At ChallengePost, we use pull zones. To set up a pull zone on MaxCDN, create an account and then follow the + Create Pull Zone link under the Zones tab. You’ll need to enter the origin URL, your primary domain, and, if desired, your custom CDN domain (such as assets.example.com or assets-example.com). Enable compression to have all your assets gzipped, another Yahoo-recommended best practice. Be sure to check out NetDNA’s article on creating a pull zone for detailed info.

create_pull_zone_on_maxcdn

Once you provision your pull zone, MaxCDN will create a CDN domain on your behalf, something like yourapp.netdna-cdn.com. You can choose to add a CNAME record pointing your own CDN domain to theirs or simply choose to use their domain directly.

That’s it! Of course, you’ll likely want to test your CDN setup before making the DNS changes, rather than hoping and praying that everything works. This is simple to do on the command line by issuing a HEAD request to a known asset on your server through the CDN host. This mimics the request behaviour of a real browser requesting the file after you made the DNS change.

If your setup is working, you would expect to see something like this:

Note the X-Cache header will indicate “MISS” the first two times the request is made because the CDN edge servers only cache files after they’ve been requested twice.

When you’re ready to make the switch live, set up your CNAME record:

Deep cuts

Depending on your business or technical constraints, you may need to go a bit further.

Assets over SSL

If your app is hosted partially over SSL, you’ll need to account for this in your asset host configuration and in your CDN configuration to take advantage of the same benefits, while avoiding mixed content warnings. You can enable SSL hosting on MaxCDN (although using a custom domain on SSL comes at an additional cost), which will come with its own CDN host.

You’ll want to make your Rails app aware of the SSL endpoint by adding some logic to your asset host configuration in config/production.rb:

For a nice write-up on dealing with complexity in your asset host configuration, check out Code Climate’s blog post on Testing Code in a Rails Initializer.

Configuring the web server

You may also need to add a directive to your web server configuration that allows cross domain requests for your assets. This can be accomplished by adding the Access-Control-Allow-Origin header in your webserver configuration. Below is an nginx configuration snippet that could work for a Rails 3.2 app:

Wrap-up

Here’s a checklist for improving delivery static assets from your Rails app:

  • Set up a cookie-free domain
  • Set up a CDN pull zone
  • Configure your Rails asset host
  • Update your DNS settings
  • Host assets from your web server, not your Rails app

Take part in Speed Awareness Month and get it done today!

Further reading:


Tags: , , , , , , , ,