why did you turn off caching?

2008-03-29 @ 22:24#

ran into a little lesson today as i was putting together a demo app for my exyus engine. it's about caching and URI design.

i was putting together an app that creates a unique URI for each person that visits the site. it's kind of 'personal URI' without forcing users to create accounts. nice idea. in my first pass at designing the URLs and testing the pages, i found that the 'launch page' (the one everyone visits) had a link on it that pointed to the user's unique URL (/codebreaker/{user-id-here}/games/). that URL was generated on the server based on a cookie setting (i know, not great, but when you don't have a login....). it worked fine.

except for the caching...

see, another thing i bundled into the web engine was automatic caching of GET/HEAD requests. that improves perf and also forces developers to do a better job of making pages cache-able in the first place. but this example was flawed. see the cached page had the unique URL in it (dang-it!). ok, so i turned off caching for that page. now users always got the proper page content - their own unique URL based on their cookie. ok, right?

not really.

see, what's *on* the page is content that is easily cached. some text. some site-wide stats and a single link (the one to the unique URL). so it *should* be cached. but then there's the link again. what i needed was a cache-able start page that *also* had a link that resulted in the unique URL for each visitor. then it hit me - 3xx!

yeah, i could use a 302 response.

so here's how it works now. first, requesting the start page makes sure the visitor has a cookie (just like before). it also has a link to point to the user's games. but this time the link *does not* have the user-id. instead it looks like this: /games/. now it's a fully cache-able page. and the magic happens when the user clicks on that link.

now, clicking on the link sends a request to the server (at /games/) and the server looks at the user's cookie to build the *proper* URL and then sends that with a 302 Found redirect with the new URL (/{user-id-goes-here}/games/). and it all works just fine. check out thew trace at the end of this post for details.

lesson here? whenever you are tempted to turn off caching of a GET request, ask yourself why. what is it one that page that requires this? quite often, you can get around no-cache issues with a little creative thinking.

http://localhost/xcs/codebreaker/
----------------------------------------------------------
GET /xcs/codebreaker/ HTTP/1.1
Host: localhost
Pragma: no-cache
Cache-Control: no-cache

HTTP/1.x 200 OK
Server: Microsoft-IIS/5.1
Date: Sun, 30 Mar 2008 03:27:39 GMT
Content-Encoding: gzip
X-Exyus: 0.6.3010.35984 2008-01
Set-Cookie: codebreaker-id=x8ca5cfca5a62d82; expires=Tue, 29-Apr-2008 03:27:39 GMT; path=/
Last-Modified: Sun, 30 Mar 2008 03:27:39 GMT
Etag: "eXDZrm9GlENsZpfYcoqOtg"
Cache-Control: public,must-revalidate,post-check=1,pre-check=2
Content-Type: text/html; charset=utf-8
Content-Length: 411

http://localhost/xcs/codebreaker/games/
----------------------------------------------------------
GET /xcs/codebreaker/games/ HTTP/1.1
Host: localhost
Referer: http://localhost/xcs/codebreaker/
Cookie: codebreaker-id=x8ca5cfca5a62d82

HTTP/1.x 302 Found
Server: Microsoft-IIS/5.1
Date: Sun, 30 Mar 2008 03:27:45 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Content-Encoding: gzip
X-Exyus: 0.6.3010.35984 2008-01
Location: /xcs/codebreaker/x8ca5cfca5a62d82/games/
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 237

http://localhost/xcs/codebreaker/x8ca5cfca5a62d82/games/
----------------------------------------------------------
GET /xcs/codebreaker/x8ca5cfca5a62d82/games/ HTTP/1.1
Host: localhost
Referer: http://localhost/xcs/codebreaker/
Cookie: codebreaker-id=x8ca5cfca5a62d82
If-Modified-Since: Sun, 30 Mar 2008 02:13:58 GMT
If-None-Match: "5goxsgXfIUNyvVmY/1hgPA"

HTTP/1.x 200 OK
Server: Microsoft-IIS/5.1
Date: Sun, 30 Mar 2008 03:27:45 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Content-Encoding: gzip
X-Exyus: 0.6.3010.35984 2008-01
Last-Modified: Sun, 30 Mar 2008 03:27:45 GMT
Etag: "5goxsgXfIUNyvVmY/1hgPA"
Cache-Control: public,must-revalidate,post-check=1,pre-check=2
Content-Type: text/html; charset=utf-8
Content-Length: 1625

code