This post is to cover conneg, or Content Negotiation.
TL;DR - use HTTP as designed and follow the rules.
Alice tries to access http://example.com/ on her iPhone. She gets back some HTML which references some images.
The markup returned to Alice contains references to 3 images. I'll just look at the first one.
Alice's iPhone made a request for /images/1 and got back a 320x80px PNG image, since we have a clever server-side component which knows about different user-agents and tries to serve the most suitable version of an image for each client.
Along comes Bob. Bob is using a Google Nexus One. He similarly requests our home page and gets back a link to /images/1. When the Nexus One requests that resource though, it gets back a 420x120px PNG, again thanks to our fancy server-side detection.
What does this do to our caching? Well, it stuffs it up completely.
- We're using a canonical URI for the image - /images/1.
- We're serving different representations of the image from the same URL.
- We cannot easily specify good HTTP expiration directives.
- There are many thousand User-Agent strings in existence.
- How different are these 2 anyway?
- Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0_1 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko)
- Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0_1 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Mobile/7A400
So alternatives to conneg?
- Use a distinct URI for every bag of bytes that the application can serve. This means that your server-side markup generation needs to be a little smarter, so that you render markup containing /images/1/320x80 and /images/1/480x120 for example (see the point in my CloudFront post about not wanting to use query string parameters for this information).
- It's still possible to support the old canonical URLs, either by continuing to perform conneg, or redirecting appropriately.
- Rev your URIs so that you can happily set Far Future Expires directives on these resources; i.e. if the image changes, give it a new URI.