According to Firebug, here are the response headers the first time the resource is retrieved:
Accept-Ranges bytes
Cache-Control public, max-age=86400
Content-Language en
Content-Length 232
Content-Location http://localhost/myapp/cacheTest.html
Content-Type text/html; charset=WINDOWS-1252
Date Wed, 05 Sep 2012 15:59:31 GMT
Last-Modified Tue, 01 May 2012 05:00:00 GMT
Server Restlet-Framework/2.0.3
Vary Accept-Charset, Accept-Encoding, Accept-Language, Accept
I click away and click back, and here are are the request headers sent to the server:
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-us,en;q=0.5
Connection keep-alive
Host localhost
Referer http://localhost/myapp/cacheTest2.html
User-Agent Mozilla/5.0 (Windows NT 5.1; rv:15.0) Gecko/20100101 Firefox/15.0
And so, naturally, the server can't send a 304 like I want, and instead sends the entire resource again.
This was happening in Firefox 14, and I thought it might be a bug, so I upgraded. But it is still happening in Firefox 15. Chrome has no problem.
I have tried both with and without an "Expires" header, it makes no difference. Firefox just refuses to send an If-Modified-Since header.
Okay, I feel like a doofus but decided to put my pride aside, and rather than just deleting this question, tell what the solution was in case anyone else ever did the same thing...
Once upon a time, in order to test something, I had turned off caching in Firefox. I turned it back on, and now it is sending the header.