Bust a cache

Ever had the following conversation with a client?

Client: "I just checked the website to see the changes you told me you'd deployed, but I can't see any. Are you sure you actually deployed it?"

You: "Are you looking at it in your browser now?"

Client: "Yes"

You: "Try holding down the Ctrl key and press F5"

Client: "Wait I'll put down my phone . . Ooooh there it is, what did you do?"

You: "Never mind".

Then you probably wished you've gotten around to implement some form of cache busting technique with the website you'd deployed.

Cache busting with a partial view

Here's how to do some cache busting using a partial view. To get ready: Log in to your favorite CMS go to the Settings section, and create a new partial view.

In the view insert the following code.

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@using File = System.IO.File
@{
    Layout = null;

    // Grab the url and find the file
    var url = (string)ViewData["href"];
    var path = Server.MapPath(url);
    var exists = File.Exists(path);

    // Determine file type
    var fileType = Path.GetExtension(path);
    if ("|.png|.gif|.jpg|.jpeg|".Contains(fileType))
    {
        fileType = "img";
    }

    // Check if the file exists
    if (exists)
    {
        // Latest write time of the file
        DateTime date = File.GetLastWriteTime(path);
        // Build an url with a parameter that reflects last write time
        url = string.Format("{0}?v={1}", url, date.Ticks);
    }
    else
    {
        fileType = string.Empty;    
    }
}

@switch (fileType)
{
    case ".css":
        <link rel="stylesheet" href="@url" />
        break;
    case ".js":
        <script type="text/javascript" src="@url"></script>
        break;
    case "img":
        <img src="@url" alt="@ViewData["altTxt"]" width="@ViewData["width"]" />
        break;
    default:
        <!-- No versionable file found at @url -->
        break;
}

The partial view to use for fingerprinting your URL

With this partial view you are able to include CSS, JavaScript and image resources in your code, that'll automatically get versioned. Every time you save the file on the server, it will get a new version parameter on the query string causing your browsers to redownload the resource.

As you see in the partial it's possible to add strings for alt-, width- and height-attributes when using the partial for images, for this I'm using the possibility for adding a ViewDataDictionary as a parameter in the call to Html.Partial. The ViewDataDictionary is a key/value dictionary made available in your Razor script as @ViewData, and it is easily declared when using the Partial view with the Html-helper in a view. The Partial can be used in these ways:

// Stylesheet
@Html.Partial("FingerprintUrl", new ViewDataDictionary{{"href", "/css/site.css"}})

// Javascript file
@Html.Partial("FingerprintUrl", new ViewDataDictionary{{"href", "/scripts/frontpage.js"}})

// Image file
@Html.Partial("FingerprintUrl", new ViewDataDictionary{{"href", "/images/20130831-DSC_0030.jpg"}, {"altTxt", "Bulbs"}, {"width", "960px"}})

How to use the partial above

Bonusinfo

It's now possible to control the caching of your static resources, and you can put this is in your Umbraco project just by creating a partial view using the excellent Umbraco backend.

But you might like to know how you can set up a really, really long caching period for your static ressources since you're now able to force the client-browsers to download the newer version every time you update the file.

This cannot be done from the Umbraco backend, so you might have to ask the webserver administrator to set it for your IIS website, or if you have access to your website filesystem via FTP or RDP or something else, you could just put the following XML-snippet in place:

<system.webServer>
    <staticContent> 
      <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365:00:00" /> 
    </staticContent>
</system.webServer>

Add cache busting to your web.config file

In an ordinary Umbraco website, the <staticContent> node will already be in place, so you should just add the clientCache node to the existing node.

This setting will cache all your static content in the client browsers for 365 days. If you're trying to do cache busting while developing your site in Visual Studio, or just want to do this using a proper HtmlHelper function, you can read my take on cache busting with the HtmlHelper.

That's it

Have a nice holiday season, and while you're away from your computer why not Bust a Move instead.

Jesper Hauge

Jesper is on Twitter as