We like to use our own site to experiment with different technologies. CDN's are nothing new, and Metal Toad has projects running on competing systems including Akamai and Level 3. Still, I think Amazon Cloudfront is an interesting offering and I wanted to give it a spin. Here's my review of the service after setting it up with Drupal:
Accept:Byte-Range
headers (important for media files) Multiple CNAMES add a little overhead with extra DNS lookups, but increase the number of parallel downloads (browsers impose a per-hostname limit). To minimize upstream bandwidth needed, these domains should be cookie-free. Since I chose subdomains of the site itself, I needed to adjust some cookie settings in Drupal:
$cookie_domain = 'www.metaltoad.com';
Thanks to some new alter hooks in Drupal 7, all you need to implement a static file CDN is hook_file_url_alter()
. There's an actively maintained CDN module, but in the spirit of inquiry I decided to implement the hook directly.
/** * Implements hook_file_url_alter(). */ function mymodule_file_url_alter(&$uri) { // Route static files to Amazon CloudFront. if ($_SERVER['HTTP_HOST'] == 'www.metaltoad.com') { if ($GLOBALS['is_https']) { // Cloudfront doesn't support custom SSL certs, so we need to use Amazon's. $cdn = 'https://abcdef12345.cloudfront.net'; } else { // Multiple hostnames to parallelize downloads. $shard = crc32($uri) % 4 + 1; $cdn = "http://static$shard.metaltoad.com"; } $scheme = file_uri_scheme($uri); if ($scheme == 'public') { $wrapper = file_stream_wrapper_get_instance_by_scheme('public'); $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri); $uri = "$cdn/$path"; } else if (!$scheme && strpos($uri, '//') !== 0) { $uri = "$cdn/$uri"; } } } /** * Implements hook_boot(). */ function mymodule_boot() { // Make sure Amazon CloudFront doesn't serve dynamic content. if (!empty($_SERVER['HTTP_X_AMZ_CF_ID']) && !strstr($_GET['q'], 'files/styles')) { header("HTTP/1.0 404 Not Found"); print '404 Not Found'; exit(); } } /** * Implements hook_css_alter(). */ function mymodule_css_alter(&$css) { // Mangle the paths slightly so that drupal_build_css_cache() will generate // different keys on HTTPS. Necessary because CDN URL varies by protocol. if ($GLOBALS['is_https']) { foreach ($css as $key => $style) { if ($style['preprocess'] && $style['type'] == 'file') { $css[$key]['data'] = './' . $style['data']; } } } }
# Set CORS header on static assets for CDN. <FilesMatch "\.(ttf|otf|eot|woff|css|css\.gz|js|js\.gz)$"> <IfModule mod_headers.c> Header set Access-Control-Allow-Origin "*" </IfModule> </FilesMatch>
The HTTPS and sharding support adds a little complexity, but overall the integration is straightforward. I'd recommend Cloudfront to anyone who wants an easy and cost-effective scalability win.