TYPO free

home

fighting for TYPO free code

Enable far future expires headers for static resources

15 Jun 2009

Web pages are becoming increasingly complex with more scripts, style sheets, images, and Flash on them. A first-time visit to a page may require several HTTP requests to load all the components. By using Expires headers these components become cacheable, which avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often associated with images, but they can and should be used on all page components including scripts, style sheets, and Flash.

You can use Apache mod_expires to set the expires headers. We've discussed this before.

Far future expires headers can be very far in the future. I use 10 years. That's right, if you fetch a stylesheet or image from this site, your browser will keep it around for 10 years.

A problem arises when using far future Expires headers. The client will not fetch a resource if it has the name and has not yet expired. So if you change your stylesheet because you use a new design, your browser will fetch the new design only if the cached resources expire, or the name of the resource has changed.

Only the static resources have 'far future' Expires headers. The generated HTML pages have normal expires headers. This means that the names of the stylesheets can be changed in the HTML source which will expire in 15 minutes or in 12 hours (or whatever you specify in the page properties of your page). This is important because by controlling the HTML, you also have control of your resource links and the format of those links.

Many people will want to 'tweak' a website from time to time and not do a full redesign. This means that they need to rename their files when they tweak the contents. There is a way around this however. Instead of changing the actual file name, you can append a URI parameter to the resource name like

<link rel="stylesheet" type="text/css" href="https://staticalien42/fileadmin/site/seed/style/main.css?v2" media="screen" />

Problem solved! We can tweak content and after we are done, we can increase the version of the version number in a constant in TypoScript. Then all we need to do is specify this version in the includeCSS and includeJS and we can send far future Expires headers with our css and js files.

You can even use full blown (major.minor.maintenance) version numbering. I hoped this would be possible in TYPO3 doing something like this:

page.includeCSS = COA
page.includeCSS {
    file1 = {$stylePath}main.css{$version}
     file1.media = screen
}

Alas, no go. Yes, there is a 'personal itch' here, but it's not that bad. The reason for the itch not being that bad is that there is an easy solution to this problem that also solves another problem described in a post that I will post later.

The problem can be solved by rewriting the HTML generated by TYPO3. We can search for all the css, js and other files for which we send far future Expires headers, and append a version parameter. One extension that does this is ja_replacer by John Angel . Here is the full blown manual in all it's glory:

Typical usage from TypoScript:

config.tx_ja_replacer {
    search {
        1="typo3temp/pics/
        2="fileadmin/
    }
    replace {
        1="https://mycdn.com/i/
        2="https://mycdn.com/f/
    }
}

Don't forget to clear the cache afterwards.

;-)

This configuration can be put in your TypoScript setup. Here is the configuraion for your JS and CSS example:

config.tx_ja_replacer {
    search {
        1=.css"
        2=.js"
    }
    replace {
        1=.css?v{$version}"
        2=.js?v{$version}"
    }
}

There, we're done. Now we have versioned file names and we can set far future expires headers on our CSS and JS file types in Apache:

<IfModule mod_expires.c>
  ExpiresActive on
  # Do not set default expires header because TYPO3 sends it's own headers, this
  # may lead to duplicate headers!
  # ExpiresDefault "access 10 years"
  ExpiresByType text/css "access 10 years"
  ExpiresByType application/x-javascript "access 10 years"
  ExpiresByType application/javascript "access 10 years"
  ExpiresByType text/javascript "access 10 years"
</IfModule> 

This idea can be extended to other file types (like images and flash) as well.

Enjoy!

Dan 16 Jun 2009, 16:31
Just a warning: If your site gets hacked (XSS injection, permissions issues, weak passwords - doesn't matter), and your JavaScript includes get injected with malicious code, visitors will cache that file, and complain to you long after you clean your site. This is coming from experience ;)

Caching for 1 week is just as good for easing the server load, and keeping the content fresh.
Michiel 18 Jun 2009, 18:21
Hi Dan,

No, they will not. When I fix the XSS, I'll upgrade the version number. So whoever downloads the files, will get a clean version.

The timeout value you set in the page properties is what you use to set the cache timeout. If that expires (ususally after 12 hours), the browser cache is refreshed.

So when a site gets hacked, the users will complain over a period of 12 - 24 hours. After that they will have the fixed version of the static files with the new version tags.

Why retrieve the same 'unchanged' file every week? Just keep it in the browser cache untill the version changes. The version change is the signal that actual content in the static files changed.
-christian 24 Jun 2009, 16:38
Sweet! Thx for sharing!
Dominique 12 Aug 2009, 10:39
With URL like https://staticalien42/fileadmin/site/seed/style/main.css?v2, NGINX does'nt compress /gzip) the main.css file, do you know a fix for this problem ? Not really important for a simple CSS, but for a complete JS framework like prototype ... it's more than 150ko for 1 js file :-(
Michiel 12 Aug 2009, 11:09
Hi Dominique,

I can't reproduce your problem. When I check my page with a tool like Yslow, all files above 1000 bytes in size are compressed by Nginx. Please double check your configuration. Does nothing compress in your case?
Commenting is closed for this item