A grid of EC2 web servers is running behind an ELB load balancer. The ELB is behind Amazon's CloudFront content delivery network. Content Delivery Networks are very new to me. My understanding is that CloudFront is supposed to speed up performance by caching static content at its "edges". But this isn't what's happening.
Consider my EC2 instances whose content should always have a lifetime of five minutes. For static content this usually means declaring the following in my web.config file:
<staticContent>
<clientCache cacheControlCustom="public" cacheControlMode="UseMaxAge" cacheControlMaxAge="00.00:05:00"/>
</staticContent>
...and for the dynamic stuff, it usually means executing the following commands against an HttpResponse object:
resp.Cache.SetCacheability(HttpCacheability.Public);
resp.Cache.SetMaxAge(TimeSpan.FromMinutes(5));
With that as background...
When my browser hits the ELB directly, everything works as expected. Firebug consistently shows that 304 (Not Modified) is returned for content that exists in the browser's cache, has passed its five minute expiration, but has not been changed on the server. Here are the response headers for a download of defs.js, for example:
HTTP/1.1 304 Not Modified
Accept-Ranges: bytes
Cache-Control: public,max-age=300
Date: Tue, 22 Apr 2014 13:54:16 GMT
Etag: "0152435d158cf1:0"
Last-Modified: Tue, 15 Apr 2014 17:36:18 GMT
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Connection: keep-alive
IIS correctly sees that the file hasn't been changed since April 15th and returns 304.
But looks what happens when the file is grabbed through CloudFront.
HTTP/1.1 200 OK
Content-Type: application/x-javascript
Content-Length: 205
Connection: keep-alive
Accept-Ranges: bytes
Cache-Control: public,max-age=300
Date: Tue, 22 Apr 2014 14:07:33 GMT
Etag: "0152435d158cf1:0"
Last-Modified: Tue, 15 Apr 2014 17:36:18 GMT
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Age: 16
X-Cache: Hit from cloudfront
Via: 1.1 0f140ef1be762325ad24a7167aa57e65.cloudfront.net (CloudFront)
X-Amz-Cf-Id: Evfdhs-pxFojnzkQWuG-Ubp6B2TC5xbunhavG8ivXURdp2fw_noXjw==
In this case CloudFront forces the browser to download the entire file again even though, as you can see:
(a) it knows the file hasn't been modified since April 15th (see Last-Modified header), and
(b) CloudFront does have a cached copy of the file on hand (see X-Cache header)
Perhaps you're wondering if my browser is sending a valid If-Modified-Since header. Indeed it is. Here are the request headers:
GET /code/shared/defs.js HTTP/1.1
Host: d2fn6fv5a0cu3b.cloudfront.net
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://d2fn6fv5a0cu3b.cloudfront.net/
Connection: keep-alive
If-Modified-Since: Tue, 15 Apr 2014 17:36:18 GMT
If-None-Match: "0152435d158cf1:0"
Cache-Control: max-age=0
It's an odd situation. If I just sit in front of my browser and keep doing page Reloads (Cmd-R), maybe about half the time CloudFront will correctly return a 304 and the other half of the time it'll incorrectly return 200 along with all of the content. Waiting for the five minute expiration before interacting with the page yields primarily 200's and only a few 304's. This odd behavior applies to all of the files (.css, .js, .png, etc.) referenced on the HTML page as well as for the containing HTML page itself. I know my app is coded properly because as mentioned above, hitting the ELB directly without going through CloudFront results in the expected 304 result. Any ideas?