Your Ad Here

Saturday, November 5, 2011

How to send the correct headers to leverage browser caching

As a follow-up to my post on compressing .php, .css and .js files without mod_gzip or mod_deflate, I’m documenting the changes I made to the .htaccess file on ardamis.com in order to speed up page load times for returning visitors and satisfy the Leverage browser caching recommendation of Google’s Page Speed Firefox/Firebug Add-on.
A great explanation of why browser caching helps the web deliver a better user experience is at betterexplained.com.
Two authoritative articles on the subject are Google’s Performance Best Practices | Optimize caching and Yahoo’s Best Practices for Speeding Up Your Web Site | Add an Expires or a Cache-Control Header.
I’d like to point out that in researching browser cashing, I came across a lot of information that contradicted the rather clear instructions from Google:
It is important to specify one of Expires or Cache-Control max-age, and one of Last-Modified or ETag, for all cacheable resources. It is redundant to specify both Expires and Cache-Control: max-age, or to specify both Last-Modified and ETag.
I’m not sure that this recommendation is entirely correct, as the W3C states that Expires and Cache-Control max-age are used in different situations, with Cache-Control max-age overriding Expires in the event of conflicts.
If a response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header, even if the Expires header is more restrictive. This rule allows an origin server to provide, for a given response, a longer expiration time to an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
It would seem that Cache-Control is the preferred method of controlling browser caching going forward.
HTTP 1.1 clients will honour “Cache-Control” (which is easier to use and much more flexible).
HTTP 1.0 clients will ignore “Cache-Control” but honour “Expires”. With “Expires” you get thus at least a bit control for these old clients.
http://www.peterbe.com/plog/cache-control_or_expires
In any event, Page Speed won’t protest if you do end up sending both Expires and Cache-Control max-age, or if you remove both Last-Modified and ETag, but I was able to get the best results with just setting Cache-Control max-age and removing the ETag.

Setting the headers in .htaccess

On Apache, configuring the proper headers can be done in the .htaccess file, using the Header directive. The Header directive requires the mod_headers module to be enabled.
I’m choosing to set a far future Expires header of one year on my images files, because I tweak the CSS and JavaScript pretty often, and don’t want those file types to be cached as long.
Add the following code to your .htaccess file to set your Cache-Control and Expires headers, adjusting the date to be one year from today.
1# Set Cache-Control and Expires headers
2<filesMatch "\\.(ico|pdf|flv|jpg|jpeg|png|gif|swf|mp3|mp4)$">
3Header set Cache-Control "max-age=2592000, private"
4Header set Expires "Sun, 17 July 2011 20:00:00 GMT"
5</filesMatch>
6<filesMatch "\\.(css|css.gz)$">
7Header set Cache-Control "max-age=604800, private"
8</filesMatch>
9<filesMatch "\\.(js|js.gz)$">
10Header set Cache-Control "max-age=604800, private"
11</filesMatch>
12<filesMatch "\\.(xml|txt)$">
13Header set Cache-Control "max-age=216000, private, must-revalidate"
14</filesMatch>
15<filesMatch "\\.(html|htm)$">
16Header set Cache-Control "max-age=7200, private, must-revalidate"
17</filesMatch>

Removing ETags in .htaccess

Most sources recommend simply removing ETags if they are not required.
Entity tags (ETags) are a mechanism that web servers and browsers use to determine whether the component in the browser’s cache matches the one on the origin server.

If you’re not taking advantage of the flexible validation model that ETags provide, it’s better to just remove the ETag altogether.
http://developer.yahoo.com/performance/rules.html#etags
Add the following code to your .htaccess file to remove ETag headers.
1# Turn off ETags
2FileETag None
3Header unset ETag

Set Expires headers with ExpiresByType (optional)

If your host has the mod_expires module enabled, you can specify Expires headers by file type. Godaddy does not have this module enabled.
1# Set Expires headers
2ExpiresActive On
3ExpiresDefault "access plus 1 year"
4ExpiresByType text/html "access plus 1 second"
5ExpiresByType image/gif "access plus 2592000 seconds"
6ExpiresByType image/jpeg "access plus 2592000 seconds"
7ExpiresByType image/png "access plus 2592000 seconds"
8ExpiresByType image/x-icon "access plus 2592000 seconds"
9ExpiresByType text/css "access plus 604800 seconds"
10ExpiresByType text/javascript "access plus 604800 seconds"
11ExpiresByType application/x-javascript "access plus 604800 seconds"

Removing the Last-Modified header in .htaccess (optional)

I’m following Google’s instructions and not removing the Last-Modified header, but if you wanted to do so, you could use:
1# Remove Last-Modified header
2Header unset Last-Modified

Busting the cache when files change

What happens when you change files and need to force browsers to load the new files? Christian Johansen offers two methods in his post on Using a far future expires header.

No comments:

Post a Comment