A client of mine continually complains that their site isn't fast enough... which is kind of odd to me because when they hired me a couple months ago, they couldn't keep the server running for 24 hours because it was caching too much and running out of memory. So I removed most of the caching, optimized their database, got them on a new server that's at least 5x as powerful (both amount of RAM and speed of processors -- they have 4x as many processors now each with nearly the same clock speed), and they're complaining that "our site has NEVER been so SLOOOOOW!".
So I'm going through the list of all the things that Google says will improve performance, in the hopes that eventually they'll either be happy with it or they'll realize that their site is doing a lot of work and there are limits on their server and some pages will take a few seconds to load, just like you can't build an entire car by hand in an hour. All the research I've done suggests that Firefox should use its cache if I've specified Cache-Control, Expires and Last-Modified headers, but no matter how I set them in ColdFusion, both Firefox and Chrome refuse to use the cache. I can manually set the 304 response header, which seems to cause Firefox to return nothing if it hasn't already cached the image. (?!!)
So I have this code. Pretty simple. Pretty much exactly what all the documentation says it should be. And FireBug consistently reports (despite restarts, cache clearing, forced reloads, etc) that the content was created seconds ago, instead of 7 days ago like I'm setting in the cfheader. Chrome on the other hand reports the Last Modified Date as a week ago, but then reports that the max-age is 0 in the request header. (?!!) (Cache is enabled in the Chrome devtools, so I know it's not that.) The response header from Chrome shows the correct max-age value. (EDIT: This was confusion on my part about the display of data in FireBug and Chrome's developer tools - so it turns out that, yes, the response headers were received by both browsers correctly.)
<cfheader name="Content-Type" value="image/png" />
<cfheader name="Cache-Control" value="max-age=604800, private, must-revalidate" />
<cfheader name="Expires" value="#getHTTPTimeString(dateadd('d', 1, now()))#" />
<cfheader name="Last-Modified" value="#getHTTPTimeString(dateadd('d', -7, now()))#" />
....
<cfcontent type="image/png" variable="#toBinary(img)#" />
Given that Chrome reports the response headers correctly, this seems like it just doesn't work the way the HTTP standards tutorials I've read (which claim to be based on the standards) say it's supposed to work... but I can't be sure. I'm not an expert on HTTP certainly, so I could be overlooking something. This is not a complex application. Hence the reason why I'm so frustrated and tearing my hair out.
Any help is greatly appreciated. Thanks!
EDIT
So to give a little more info on the accepted answer, here's what my solution looks like in ColdFusion. The above code basically stays as it is in the page (index.cfm for example). Then to make use of the caching hints returned by the browsers, I have something like this in an Application.cfc
<cfcomponent output="false">
<cffunction name="onRequest" access="public" output="true">
<cfargument name="targetPage" type="string" required="true" />
<cfset var modDate = getIfMOdifiedSince() />
<cfif isDate(modDate) and datediff('d', modDate, now()) lte 7>
<!--- the browser has a cached copy that's less than a week old, let the browser use it --->
<cfheader name="Content-Type" value="image/png" />
<cfheader statuscode="304" statustext="Not Modified" />
<cfelse>
<!--- the browser hasn't seen it in 7 days (or possibly ever), return the image --->
<cfheader name="Last-Modified" value="#getHTTPTimeString(now())#" />
<cfinclude template="#targetPage#" />
</cfif>
</cffunction>
<cffunction name="getIfModifiedSince" access="private" output="false" returntype="string">
<cfset var head = getHTTPRequestData().headers />
<cfreturn iif(structKeyExists(head, "if-modified-since"), "head['if-modified-since']", de("")) />
</cffunction>
</cfcomponent>
This may be a bit simpler than what you need. This particular code doesn't actually check to see if the image has been modified, because that's not something we're really worried about. This app only serves a specific set of images, so we know anything being served from this app will be a PNG so we can safely set the content type header to image/png and because the images really don't change, I'm not bothering with any kind of actual date checking. I probably could really allow the images to live indefinitely, but for now I'm letting the browser have them for a week at a time. I may increase that duration later as their business grows.
The real crux of the work, the thing that makes the caching work is that the onRequest() method checks the if-modified-since request header and that when present and within the desired timespan, it truncates the entire request by NOT including the target page (so none of the remaining page code will execute) and instead only sets the 403 Not Modified status response header.
In the long-run, just as I suspected, it is actually a really simple thing, it's apparently just widely misunderstood by people who create online tutorials about it.