FYI, the fix was in the ASP.NET code for Tile.aspx:
Response.Cache.SetCacheability( HttpCacheability.Public ); // was doing this
Response.Cache.SetExpires( DateTime.Now.AddDays( 1 ) ); // was doing this
Response.Cache.SetValidUntilExpires( true ); // wasn't doing this
I was also misinterpreting the meaning of Response.Cache.VaryByParams (which populates the HTTP "Vary:" header). I was interpreting the semantics as "re-request if these params are different" rather than correct semantics: "re-request even if these params are the same". So I removed calls to that, and everything appears happier now.
So what does that mean? In IE, try zooming all the way in to 64 pixels/parsec then out to 1/32 pixels/parsec, using the mouse wheel. Then, once all the tiles are cached, you can repeat the zoom in/zoom out without waiting for the images to re-load. Makes a great demo! Ω