I have not been blogging for a long long time and was trying to find out a topic for my new post. I thought I can write a post on Rack::StaticCache – a Rack middleware that I contributed to rack-contrib a couple of months back.
If you have worked with any Ruby based framework recently then you probably know what Rack is. If you don't know, in one sentence, Rack is a common interface between webservers supporting Ruby and Ruby frameworks. You can check out the links at the official Rack site for more details.
It was my first contribution to the rack-contrib project and I really liked the slick API Rack provides for developing middlewares/utilities. If you are not familiar with the concept of middleware, in short, middleware is a communication infrastructure that allows components to be plugged in. And Rack middlewares are components that can be plugged into Rack and they can intercept and modify the request and the response of the webserver.
So what does Rack::StaticCache middleware do? As described in the documentation -
“Rack::StaticCache modifies the response headers to facilitate client and proxy caching for static files that minimizes HTTP requests and improves overall load times for second time visitors”
Sounds cool, right? ;)
One problem of long term HTTP caching is that - once a static content is stored in a client/proxy it's kind of stored forever (until the cache expires or is deleted). Suppose, we have an image file test.png and we have modified its header so that the client or proxy stores it in the cache for a long period of time. And later we have change the image file itself in the server. Now the client who already has stored this image in its cache will serve the old image from the cache. So the client will not see the change. :(
So we need to somehow tell the browser to fetch the new content when the content is modified in the server. One way to enforce the browser to fetch the latest content and ignore the cache is to rename the file. But that's not really neat.
Alternatively, we can add a version number into the URL to the content to bypass the caches- so that we don't need to actually rename the file. Rack::StaticCache by default handles version numbers in the filenames which makes this task pretty easy. As an example, if you are using Rack::StaticCache, http://example.com/images/test-1.0.0.png and http://example.com/images/test-2.0.0.png both will refer to the same image file http://example.com/images/test.png. So we will just need to change the URL of a content whenever it is modified and Rack::StaticCache will handle the rest.
Another way to bypass the cache is adding the version number in a field-value pair in the URL query string. As an example, http://example.com/images/test.png?v=1.0.0. In that case, we can set the option :versioning in Rack::StaticCache to false to avoid unnecessary regular expression calculations (see examples below).
It's better to keep the current version number in some configuration file and append it in every static content's URL. So each time we modify our static contents, we just have to change the version number to ask the browser to pull the content from the server. We can use Rack::Deflater middleware along with Rack::StaticCache for further improvements in page loading time.
Here goes some example usages:
use Rack::StaticCache, :urls => ["/images", "/css", "/js", "/documents*"], :root => "statics"
will serve all requests beginning with /images, /css or /js from the directory "statics/images", "statics/css", "statics/js". All the files from these directories will have modified headers to enable client/proxy caching, except the files from the directory "statics/documents". Append a * (asterisk) at the end of the pattern if you want to disable caching for that directory. In that case, plain static contents will be served with default headers.
use Rack::StaticCache, :urls => ["/images"], :duration => 2, :versioning => false
will serve all requests beginning with /images under the current directory (default for the option :root is current directory). All the contents served will have cache expiration duration set to 2 years in headers (default for :duration is 1 year), and Rack::StaticCache will not compute any versioning logics (:versioning defaults to true).
You can find the Rack::StaticCache in rack/rack-contrib repository in github. rack-contrib is a project started and managed by Ryan Tomayko, it is a collection of rack middlwares contributed by the ruby community. You can also clone rack/rack-contrib, write your own middleware and submit a pull request. Happy racking!!
Reader Commentary
No one have commented so far.
There are currently no comments. Come on be the first one!