February 3, 2015
What Cache Validators Look Like in Action
Cache validators are request and response headers that indicate the validity of a requested file against a local browser cache. When a browser opens a file for the first time, it stores it in it’s local cache based on various cache validators. This way subsequent requests don’t have to be loaded from the server, thus saving bandwidth.
So what does a request like this look like?
Looking into Firefox browser and how it sends request (using Inspect Element), you can see that request headers are the most important part of the request:
This request corresponds with following CURL line:
~$ curl http://domain.com/file.ext
This will get a response from the server with status code 200 showing that the requested file was delivered:
At this point, the file we requested is cached in the browser’s local cache. Now when the next request comes in (reload page), the browser will be aware of a previously cached file. And for optimization purposes, it will include the “Last-Modified” header value in a request. This header value will ask if the file was changed since the last time it was requested.
The request corresponds with the following CURL line:
~$ curl http://www.domain.com/file.ext -H ‘If-Modified-Since: Wed, 09 Oct 2013 01:35:39 GMT’
The request headers from the browser look like the following:
This request is literarily asking server “Hey, did this file get changed since this time: ‘Wed, 09 Oct 2013 01:35:39 GMT’?”
If the file is unchanged, the browser will get a response of status code 304. In english, this translates to: “Nope, this file didn’t get change since that time, load it from your local cache”.
The response headers look like so:
Last-Modified is one header that is providing cache validation; however, there is another one equally important called ETag (Entity Tag). The purpose of this this header is the same and it’s value is used to determine validity of a requested file.
The ETag contains a value that identifies it’s content and it and changes accordingly if content is changed. The algorithm used to generate ETags is a collision-resistant hash function generated on the base of a file content.
A request with Etag corresponds with the following CURL example:
~$ curl http://www.domain.com/file.ext -H ‘If-None-Match: 123456789’
The resulting response header from the server should be 304 if the file’s ETag value on the server matches the ETag the browser contains.
How to Specify Cache Validation Headers
Web servers add the Last-Modified header to your assets automatically. However you can add it yourself on a dynamic file in which case you’d need to code the header within the dynamic file itself:
@header ('Last-Modified: ' . gmstrftime("%a, %d %b %Y %H:%M:%S GMT", time() - 20)); //60*60*3
As for ETag headers, assuming apache is the web server you are serving assets from, ETag would not be necessary as the validation is already handled by Last-Modified.
Setting ETag directive consists of:
- INode – the file system object identificator
- MTime – the time the file was last modified
- Size – represents file size in bytes
The Apache configuration directive looks like this:
<directory usr="" local="" httpd="" htdocs=""> FileETag INode MTime Size </directory>
And here is an example ETag header:
With 10690a1 equaling INode, 5f1 equaling MTime, and 80d90aa4 equaling Size.
How to Direct Cache Validation Issues
If you get a cache validation suggestion from a performance testing tool like Pingdom, it probably means you have some external 3rd party resources being loaded from buckets where Last-Modified and/or ETag headers are stripped off intentionally.
If the location is not under your control, ignore this suggestion as access control limits are in place.
What If Last-Modified Isn’t In Response Headers List?
Depending on who managed your server, it is possible that this header was removed due to false conviction of Last-Modified being bad for performance. If this is the case, you should see “unset” directive with following format:
Header unset Last-Modified
This should be removed (from htaccess or config file). If needed, you should also restart server to apply config reset (restart is needed if you are changing config file only – not htaccess).
To confirm header is set ,running CURL as follows should produce response headers as described:
~$ curl -I https://www.domain.com/file.png HTTP/1.1 200 OK Date: Thu, 15 Jan 2015 02:48:46 GMT Content-Type: image/png Content-Length: 51851 Connection: keep-alive Last-Modified: Wed, 14 Jan 2015 23:53:03 GMT Server: NetDNA-cache/2.2 Expires: Thu, 22 Jan 2015 02:48:46 GMT Cache-Control: max-age=604800 X-Cache: HIT Accept-Ranges: bytes