Far Future Expires Headers For Ruby On Rails With Nginx


Creative Commons License seier+seier

Browsers load static images from your website again and again if your web server does not send an expires header with a date far in the future. To avoid that unnecessary traffic on your servers and unnecessary load times for your users, it’s a good idea to let your nginx send those expires headers. But, what if you tell the browser not to come back for your css files until next year and now you change some style within it? The Rails URL helpers automatically attach a timestamp representing the last modification date to each URL (like ?2345346654). That changes the URL every time you deploy making the browser load the modified file. In this post, I want to show you all the not so obvious things to consider when you introduce far future expires headers with rails and nginx.

Far Future Expires Headers In nginx

The first thing you have to do is to tell your nginx to send those expires headers with all your images, css and javascript files. Just add the following section to your server configuration in nginx:

	server {
	    ...
	    location ~* .(ico|css|js|gif|jp?g|png) {
	        if ($args ~ [0-9]+) {
	           expires max;
	           break;
	        }
	    }
	    ...
	}

First, you match the location with a regular expression catching all your static resources. If you have additional file types you want to equip with a far future expires header, just add them to the regex. The important part, which a lot of people get wrong, is to additionally check for the timestamp parameter. You only want to add the expires header to files if the URL has the timestamp attached. Otherwise you have no chance to push out your changes anymore! In quite a few places around the net you find people attaching ?[0-9]+$ to the regex matching the location. This cannot work as location does not include the $args.

Adding Timestamps To URLs In CSS Files

Using far future expires headers in nginx only for files having a timestamp is great for every URL you generate using the rails helpers. But what about e.g. background images you define in your static css files? As they do not have a timestamp attached they will not get any expires headers and therefore will not be cached by the browsers. Especially if you use larger image maps for css sprites, that hurts. You can use the yslow and live HTTPheaders firefox plugins to validate the headers.

There are a few ways to add timestamps to URLs in your css:

We are currently using the monkey patch, but I would recommend you to do it in an initializer.

Using far future expires headers with rails and nginx is simple and very effective. Your servers and your users will love it!

2 thoughts on “Far Future Expires Headers For Ruby On Rails With Nginx

  1. Note that if you’re on Rails 3.1+ and using the asset pipeline, your assets will be tagged with an MD5 hash of the file content like `bla-52a34d5f4.png` during precompilation. The timestamping no longer happens, I think mostly because clocks might differ if you’re using multiple servers or CDNs.

    I wrote up some more background about this here:

    https://makandracards.com/makandra/11345-your-application-is-slow-because-browsers-cannot-cache-your-assets

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.