Everyone knows that they should follow the YSlow guidelines and serve up javascript, css and images:
- minified to reduce size
- combined into single files to reduce requests
- gzipped to reduce size
- from a CDN to reduce latency
- with far-future expires headers to increase caching
It's tedius to find and install minifier libraries, setup a CDN, synchronize your assets to the CDN, reconfigure your web server expires and gzip settings and recode your html. And every time you change assets or start a new project, you have to do it all over again.
InstaCDN provides a simple REST API to easily minify, combine, gzip and push your css, js and image assets into the Amazon Cloudfront CDN with far-future expiration headers.
InstaCDN will minify, combine, gzip and serve your css from Amazon's Cloudfront CDN with long expiration headers.
It will automagically detect background images referenced from the css, put them in the CDN and rewrite the css to use the new CDN image urls.
# If this is a snippet of html from http://news.ycombinator.com/
<link href="http://ycombinator.com/news.css" rel="stylesheet" type="text/css">
# You can get an instacdn url by calling the api like this:
$ curl http://instacdn.com/css \
-F 'files=http://ycombinator.com/news.css'
http://cf.instacdn.com/css/b2e73008b2a72e8c33a59e5e08195e62.css
# and replace that html with
<link href="http://cf.instacdn.com/css/b2e73008b2a72e8c33a59e5e08195e62.css" rel="stylesheet" type="text/css">
# To get a gzip'd version, add 'gzip=1' to the api request.
# Your server will need to dynamically detect 'Accept-Encoding: gzip'
# from clients as they request the html and decide which instacdn
# url to include in the <link> tag
$ curl http://instacdn.com/css \
-F 'gzip=1' \
-F 'files=http://ycombinator.com/news.css'
http://cf.instacdn.com/gz/css/b2e73008b2a72e8c33a59e5e08195e62.css
# ... but really, all you need to do is prepend '/gz' on the path
Let's take a peek at what's coming back from the CDN. The css is all combined and minified. It's gzipped. It's got great caching headers (Cache-Control, Expires, ETag). Browsers will cache it for a year. All the image urls are now cloudfront urls, so the image assets are served efficiently and with their own far-future expires headers.
$ curl -i http://cf.instacdn.com/css/b2e73008b2a72e8c33a59e5e08195e62.css HTTP/1.0 200 OK x-amz-id-2: VEFaCr/Zx1gP1YPuvb7WXqf/iUWiP8OxRRmGNQ8xxRkiPYvvD+YnxXxzZ1zldZEf x-amz-request-id: 11B398C61237288D Date: Tue, 04 May 2010 12:50:24 GMT Cache-Control: max-age=86400000 Content-Encoding: gzip Expires: Wed, 04 May 2011 07:37:59 GMT Last-Modified: Tue, 04 May 2010 12:38:01 GMT ETag: "6d86b065e87ef91eacf87963840ae723" Content-Type: text/css Content-Length: 441 Server: AmazonS3 X-Cache: Miss from cloudfront X-Amz-Cf-Id: 4b5390362af27a3a94b3d55ca6965bbec... Via: 1.0 04c6b7d69b6a9aa32368bf8fd71029a0.cloudfront.net:11180... Connection: close ...
To minify, combine, and serve your javascript from Amazon's Cloudfront CDN with long expiration headers:
# If this is a snippet of html from http://twitter.com/ev
<script src="http://a0.twimg.com/a/1271891196/javascripts/twitter.js?1271892080" type="text/javascript"></script>
<script src="http://a3.twimg.com/a/1271891196/javascripts/lib/jquery.tipsy.min.js?1271892080" type="text/javascript"></script>
<script src="http://a3.twimg.com/a/1271891196/javascripts/lib/gears_init.js?1271892080" type="text/javascript"></script>
<script src="http://a1.twimg.com/a/1271891196/javascripts/geov1.js?1271892080" type="text/javascript"></script>
<script src="http://a2.twimg.com/a/1271891196/javascripts/api.js?1271892080" type="text/javascript"></script>
<script src="http://a0.twimg.com/a/1271891196/javascripts/lib/mustache.js?1271892080" type="text/javascript"></script>
<script src="http://a1.twimg.com/a/1271891196/javascripts/dismissable.js?1271892080" type="text/javascript"></script>
# You can get an instacdn url by calling the api like this.
# Note that we just use spaces to separate the multiple "files arguments.
$ curl http://instacdn.com/js \
-F 'files=http://a0.twimg.com/a/1271891196/javascripts/twitter.js
http://a3.twimg.com/a/1271891196/javascripts/lib/jquery.tipsy.min.js
http://a3.twimg.com/a/1271891196/javascripts/lib/gears_init.js
http://a1.twimg.com/a/1271891196/javascripts/geov1.js
http://a2.twimg.com/a/1271891196/javascripts/api.js
http://a0.twimg.com/a/1271891196/javascripts/lib/mustache.js
http://a1.twimg.com/a/1271891196/javascripts/dismissable.js'
http://cf.instacdn.com/js/f8be85a5210982754279362fe7a740e7.js
# and replace that html with
<script src="http://cf.instacdn.com/js/f8be85a5210982754279362fe7a740e7.js" type="text/javascript"></script>
# To get a gzip'd version, add 'gzip=1' to the api request.
# Your server will need to dynamically detect 'Accept-Encoding: gzip'
# from clients as they request the html and decide which instacdn
# url to include in the <script> tag
$ curl http://instacdn.com/js \
-F 'gzip=1' \
-F 'files=http://a0.twimg.com/a/1271891196/javascripts/twitter.js
http://a3.twimg.com/a/1271891196/javascripts/lib/jquery.tipsy.min.js
http://a3.twimg.com/a/1271891196/javascripts/lib/gears_init.js
http://a1.twimg.com/a/1271891196/javascripts/geov1.js
http://a2.twimg.com/a/1271891196/javascripts/api.js
http://a0.twimg.com/a/1271891196/javascripts/lib/mustache.js
http://a1.twimg.com/a/1271891196/javascripts/dismissable.js'
http://cf.instacdn.com/gz/js/f8be85a5210982754279362fe7a740e7.js
# ... but really, all you need to do is prepend '/gz' on the path
Using InstaCDN to serve your images from Amazon's Cloudfront CDN is trivial.
# If you have an image like this on http://news.ycombinator.com/
<img src="http://ycombinator.com/images/grayarrow.gif" ... />
# You can get an instacdn url
$ curl http://instacdn.com/img \
-F 'file=http://ycombinator.com/images/grayarrow.gif'
http://cf.instacdn.com/gif/7e6bfa0d7419ef49e2a58c199702f9fa.gif
Just ping the api again, and you'll get back a different url. The url contains an md5 hash of the content. If you change the content, the url changes. If you don't change the content, the api will give you back the same url every time.
It's expensive when you call the api. Try and cache api responses in your server with memcache and only call it whenever you push a new version of your site.
Something like this will work fine for python/django as long as you flush memcached whenever you release a new version of your site.
import urllib
from django.core.cache import cache
def cached_get(url):
# check memcached
res= cache.get(url)
if res:
return res
res= urllib.urlopen(url).read()
# cache it in memcached forever
cache.set(url, res, 60*60*24*365)
return res
cached_get('http://instacdn.com/img&file=http://ycombinator.com/images/grayarrow.gif')
It's cheap when your users fetch the cf.instacdn.com urls since cloudfront does all the heavy lifting.
No catch. It just works. It's free for now.
It's only been lightly tested against css/js/images that I control as part of my own projects. Beware.
Please don't abuse it.