Setting ETag Headers Regardless Of File Content In ColdFusion

Last week, at InVision, while trying to improve the overall experience of one of our application pages, I started looking into ETag header values to decrease page load times. The first post that I came across (or rather, that I came across "again") was this one by David Boyer. In it, David outlines what ETag headers do and how to apply them in ColdFusion. Only, in my case, I didn't want the value of the ETag header to be based on the file content - I wanted it to be based on the state of the application.

In my applications, I tend to store an "application version" number. This version number is simply a numeric representation of the date/time on which the application was last initialized. This turns out to be a super useful property when it comes to problems like managing caching and keeping sessions in sync with the application.

Application.cfc - Our ColdFusion Application Framework

<cfcomponent

output="false"

hint="I define the application settings and event handlers.">

<!--- Define the application settings. --->

<cfset this.name = hash( getCurrentTemplatePath() ) />

<cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />

<cfset this.sessionManagement = false />

<!--- Turn of debugging. --->

<cfsetting showdebugoutput="false" />

<cffunction

name="onApplicationStart"

access="public"

returntype="boolean"

output="false"

hint="I initialize the application.">

<!---

Store a version number for the application - a numeric

version of the date on which it was initialized.

--->

<cfset application.versionNumber = (now() * 1) />

<!--- Return true so the application can finish loading. --->

<cfreturn true />

</cffunction>

<cffunction

name="onRequestStart"

access="public"

returntype="boolean"

output="false"

hint="I initialize the request.">

<!--- Check to see if we need to manually reset the app. --->

<cfif structKeyExists( url, "init" )>

<cfset this.onApplicationStart() />

</cfif>

<!--- Return true so the request can finish loading. --->

<cfreturn true />

</cffunction>

</cfcomponent>

As you can see, this "versionNumber" property simply gets set every time the onApplicationStart() event handler runs, whether implicitly executed by the ColdFusion Application Framework, or explicitly executed by your request logic.

Since I wanted to define an ETag header for a file, regardless of the content, I figured that I could use a combination of the file path and this application version number. Together, these two values would provide a unique yet controllable ETag input.

To test this, I set up a simple page that adds an ETag header to a response that outputs the current time.

As you can see, the ETag hash is powered by the file path and the application version number. This hash value is then compared against the "If-None-Match" header provided by the browser. If these two values match, the request is simply terminated with a "Not Modified" response. In this approach, not only do we detach the ETag value from the file content, we remove the overhead of processing the request in order to determine the ETag response.

To pull this together, I created an index file that loads the above ColdFusion page as an IFrame:

<!doctype html>

<html>

<head>

<title>Programmatically Setting ETags In ColdFusion</title>

</head>

<body>

<h1>

Programmatically Setting ETags In ColdFusion

</h1>

<iframe

src="./frame.cfm"

width="500"

height="100">

</iframe>

<p>

<a href="./index.cfm">Refresh page</a>

</p>

<p>

<a href="./index.cfm?init=true">Refresh page with Init</a>

</p>

</body>

</html>

As you can see, in addition to the IFrame, it has two test links: one to refresh the page; and, another to refresh the page while re-initializing the application (ie. defining the "init" query string). And, as you can see in the network activity below, subsequent page loads show a cached version of the frame:

I definitely understand basing ETag headers on file content; however, in my applications, we tend to use a re-initialization event to bust caches. As such, using the application version number in combination with the path of a given file provides an easy way for me to use ETag headers with little overhead.

As a final note, I should mention that I am also adding "Expires" headers to my response. I don't do that in this demo (so that I can see the HTTP requests being made); however, in production, I would also add an Expires header to minimize the number of HTTP requests that need to be made.

Reader Comments

Hey Ben, thanks for the wonderful tutorial. I haven't been able to get this to work in safari. For some reason safari is not sending an http_if_none_match in the request header for my cfm. Can you confirm this or am I just going crazy?

Hey Ben, got a quick question for you. I have added a cache manifest file for HTML5 coding and made use of the cfcache tag as such; placed above the body tag.

When placing the cfcache with the function use Cache is true and with a trailing slash or a closing /cfcache tag wrapped around my content to cache; it produces duplication errors with my cfform, masks and cfhtml tag. I have been discussing this one with Ray too. I believe it to be a CF10 bug.

But with no useCache is true, no trailing slash or /cfcache closing tag it no-longer duplicates the output of those tags and files.

I was also wondering whether the meta cache-controls are needed if the cfheader tags are being used or if the HTML5 manifest attribute is being used on the pages or if the cfcache is needed at all?

The server is caching the pages and when adding the manifest file with meta tag's the browser is caching the files needing to be cached. Test it...

I am the co-founder and lead engineer at InVision App, Inc — the world's leading prototyping,
collaboration & workflow platform. I also rock out in JavaScript and ColdFusion 24x7 and I dream about
promise resolving asynchronously.